Merge pull request #120 from Retrospring/113-groups2lists

Rename Groups to Lists
This commit is contained in:
Georg Gadinger 2020-05-27 19:47:31 +02:00 committed by GitHub
commit bb0d85d613
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 423 additions and 472 deletions

View File

@ -19,7 +19,7 @@
# local requires to be seen by everyone:
#= require_tree ./answerbox
#= require_tree ./questionbox
#= require groups
#= require lists
#= require inbox
#= require memes
#= require notifications

View File

@ -1,25 +1,25 @@
($ document).on "click", "input[type=checkbox][name=gm-group-check]", ->
($ document).on "click", "input[type=checkbox][name=gm-list-check]", ->
box = $(this)
box.attr 'disabled', 'disabled'
groupName = box[0].dataset.group
listName = box[0].dataset.list
count = Number ($ "span##{groupName}-members").html()
count = Number ($ "span##{listName}-members").html()
boxChecked = box[0].checked
count += if boxChecked then 1 else -1
$.ajax
url: '/ajax/group_membership'
url: '/ajax/list_membership'
type: 'POST'
data:
user: box[0].dataset.user
group: groupName
list: listName
add: boxChecked
success: (data, status, jqxhr) ->
if data.success
box[0].checked = if data.checked? then data.checked else !boxChecked
($ "span##{groupName}-members").html(count)
($ "span##{listName}-members").html(count)
showNotification data.message, data.success
error: (jqxhr, status, error) ->
box[0].checked = false
@ -29,19 +29,19 @@
box.removeAttr "disabled"
$(document).on "keyup", "input#new-group-name", (evt) ->
$(document).on "keyup", "input#new-list-name", (evt) ->
if evt.which == 13 # return key
evt.preventDefault()
$("button#create-group").trigger 'click'
$("button#create-list").trigger 'click'
($ document).on "click", "button#create-group", ->
($ document).on "click", "button#create-list", ->
btn = $(this)
btn.button "loading"
input = ($ "input#new-group-name")
input = ($ "input#new-list-name")
$.ajax
url: '/ajax/create_group'
url: '/ajax/create_list'
type: 'POST'
data:
name: input.val()
@ -49,7 +49,7 @@ $(document).on "keyup", "input#new-group-name", (evt) ->
dataType: 'json'
success: (data, status, jqxhr) ->
if data.success
($ "ul.list-group.groups--list").append(data.render)
($ "#lists-list ul.list-group").append(data.render)
input.val ''
showNotification data.message, data.success
error: (jqxhr, status, error) ->
@ -59,14 +59,14 @@ $(document).on "keyup", "input#new-group-name", (evt) ->
btn.button "reset"
($ document).on "click", "a#delete-group", (ev) ->
($ document).on "click", "a#delete-list", (ev) ->
ev.preventDefault()
btn = $(this)
group = btn[0].dataset.group
list = btn[0].dataset.list
swal
title: translate('frontend.group.title')
text: translate('frontend.group.text')
title: translate('frontend.list.title')
text: translate('frontend.list.text')
type: "warning"
showCancelButton: true
confirmButtonColor: "#DD6B55"
@ -75,14 +75,14 @@ $(document).on "keyup", "input#new-group-name", (evt) ->
closeOnConfirm: true
, ->
$.ajax
url: '/ajax/destroy_group'
url: '/ajax/destroy_list'
type: 'POST'
data:
group: group
list: list
dataType: 'json'
success: (data, status, jqxhr) ->
if data.success
($ "li.list-group-item#group-#{group}").slideUp()
($ "li.list-group-item#list-#{list}").slideUp()
showNotification data.message, data.success
error: (jqxhr, status, error) ->
console.log jqxhr, status, error

View File

@ -3,18 +3,11 @@ $(document).on "click", "button[name=qb-all-ask]", ->
btn.button "loading"
$("textarea[name=qb-all-question]").attr "readonly", "readonly"
rcptSelect = ($ 'select[name=qb-all-rcpt]')
rcpt = if rcptSelect.length > 0
rcptSelect.first().val()
else
'followers'
$.ajax
url: '/ajax/ask'
type: 'POST'
data:
rcpt: rcpt
rcpt: 'followers'
question: $("textarea[name=qb-all-question]").val()
anonymousQuestion: false
success: (data, status, jqxhr) ->

View File

@ -1,4 +1,4 @@
class Ajax::GroupController < AjaxController
class Ajax::ListController < AjaxController
def create
@response[:status] = :err
@ -13,35 +13,35 @@ class Ajax::GroupController < AjaxController
rescue ActionController::ParameterMissing => e
NewRelic::Agent.notice_error(e)
@response[:status] = :toolong
@response[:message] = I18n.t('messages.group.create.noname')
@response[:message] = I18n.t('messages.list.create.noname')
return
end
params.require :user
begin
target_user = User.find_by_screen_name!(params[:user])
group = Group.create! user: current_user, display_name: params[:name]
list = List.create! user: current_user, display_name: params[:name]
rescue ActiveRecord::RecordInvalid => e
NewRelic::Agent.notice_error(e)
@response[:status] = :toolong
@response[:message] = I18n.t('messages.group.create.toolong')
@response[:message] = I18n.t('messages.list.create.toolong')
return
rescue ActiveRecord::RecordNotFound => e
NewRelic::Agent.notice_error(e)
@response[:status] = :notfound
@response[:message] = I18n.t('messages.group.create.notfound')
@response[:message] = I18n.t('messages.list.create.notfound')
return
rescue ActiveRecord::RecordNotUnique => e
NewRelic::Agent.notice_error(e)
@response[:status] = :exists
@response[:message] = I18n.t('messages.group.create.exists')
@response[:message] = I18n.t('messages.list.create.exists')
return
end
@response[:status] = :okay
@response[:success] = true
@response[:message] = I18n.t('messages.group.create.okay')
@response[:render] = render_to_string(partial: 'modal/group/item', locals: { group: group, user: target_user })
@response[:message] = I18n.t('messages.list.create.okay')
@response[:render] = render_to_string(partial: 'modal/list/item', locals: { list: list, user: target_user })
end
def destroy
@ -53,20 +53,20 @@ class Ajax::GroupController < AjaxController
return
end
params.require :group
params.require :list
begin
Group.where(user: current_user, name: params[:group]).first.destroy!
List.where(user: current_user, name: params[:list]).first.destroy!
rescue ActiveRecord::RecordNotFound => e
NewRelic::Agent.notice_error(e)
@response[:status] = :notfound
@response[:message] = I18n.t('messages.group.destroy.notfound')
@response[:message] = I18n.t('messages.list.destroy.notfound')
return
end
@response[:status] = :okay
@response[:success] = true
@response[:message] = I18n.t('messages.group.destroy.okay')
@response[:message] = I18n.t('messages.list.destroy.okay')
end
def membership
@ -79,30 +79,30 @@ class Ajax::GroupController < AjaxController
end
params.require :user
params.require :group
params.require :list
params.require :add
add = params[:add] == 'true'
begin
group = current_user.groups.find_by_name!(params[:group])
list = current_user.lists.find_by_name!(params[:list])
rescue ActiveRecord::RecordNotFound => e
NewRelic::Agent.notice_error(e)
@response[:status] = :notfound
@response[:message] = I18n.t('messages.group.membership.notfound')
@response[:message] = I18n.t('messages.list.membership.notfound')
return
end
target_user = User.find_by_screen_name!(params[:user])
if add
group.add_member target_user if group.members.find_by_user_id(target_user.id).nil?
list.add_member target_user if list.members.find_by_user_id(target_user.id).nil?
@response[:checked] = true
@response[:message] = I18n.t('messages.group.membership.add')
@response[:message] = I18n.t('messages.list.membership.add')
else
group.remove_member target_user unless group.members.find_by_user_id(target_user.id).nil?
list.remove_member target_user unless list.members.find_by_user_id(target_user.id).nil?
@response[:checked] = false
@response[:message] = I18n.t('messages.group.membership.remove')
@response[:message] = I18n.t('messages.list.membership.remove')
end
@response[:status] = :okay

View File

@ -27,7 +27,7 @@ class Ajax::QuestionController < AjaxController
params.require :anonymousQuestion
params.require :rcpt
is_never_anonymous = user_signed_in? && (params[:rcpt].start_with?('grp:') || params[:rcpt] == 'followers')
is_never_anonymous = user_signed_in? && params[:rcpt] == 'followers'
begin
question = Question.create!(content: params[:question],
@ -50,22 +50,7 @@ class Ajax::QuestionController < AjaxController
end
if params[:rcpt] == 'followers'
unless current_user.nil?
QuestionWorker.perform_async params[:rcpt], current_user.id, question.id
end
elsif params[:rcpt].start_with? 'grp:'
unless current_user.nil?
begin
current_user.groups.find_by_name!(params[:rcpt].sub 'grp:', '')
QuestionWorker.perform_async params[:rcpt], current_user.id, question.id
rescue ActiveRecord::RecordNotFound => e
NewRelic::Agent.notice_error(e)
question.delete
@response[:status] = :not_found
@response[:message] = I18n.t('messages.question.create.not_found')
return
end
end
QuestionWorker.perform_async(current_user.id, question.id) unless current_user.nil?
else
u = User.find_by_id(params[:rcpt])
if u.nil?

View File

@ -1,15 +0,0 @@
class GroupController < ApplicationController
before_action :authenticate_user!
def index
@group = current_user.groups.find_by_name!(params[:group_name])
@timeline = @group.cursored_timeline(last_id: params[:last_id])
@timeline_last_id = @timeline.map(&:id).min
@more_data_available = !@group.cursored_timeline(last_id: @timeline_last_id, size: 1).count.zero?
respond_to do |format|
format.html
format.js { render layout: false }
end
end
end

View File

@ -0,0 +1,15 @@
class ListController < ApplicationController
before_action :authenticate_user!
def index
@list = current_user.lists.find_by_name!(params[:list_name])
@timeline = @list.cursored_timeline(last_id: params[:last_id])
@timeline_last_id = @timeline.map(&:id).min
@more_data_available = !@list.cursored_timeline(last_id: @timeline_last_id, size: 1).count.zero?
respond_to do |format|
format.html
format.js { render layout: false }
end
end
end

View File

@ -61,13 +61,13 @@ class UserController < ApplicationController
end
# endregion
# region Groups
def groups
# region Lists
def lists
@user = User.where('LOWER(screen_name) = ?', params[:username].downcase).first!
@groups = if current_user == @user
@user.groups
@lists = if current_user == @user
@user.lists
else
@user.groups.where(private: false)
@user.lists.where(private: false)
end.all
end
# endregion

View File

@ -166,7 +166,7 @@ module ApplicationHelper
user_title user, "comments"
end
def group_title(group)
generate_title group.name
def list_title(list)
generate_title list.name
end
end

View File

@ -1,4 +0,0 @@
class GroupMember < ApplicationRecord
belongs_to :user
belongs_to :group
end

View File

@ -1,10 +1,10 @@
# frozen_string_literal: true
class Group < ApplicationRecord
include Group::TimelineMethods
class List < ApplicationRecord
include List::TimelineMethods
belongs_to :user
has_many :group_members, dependent: :destroy
has_many :list_members, dependent: :destroy
validates :name, length: { minimum: 1 }
validates :display_name, length: { maximum: 30 }
@ -15,13 +15,13 @@ class Group < ApplicationRecord
self.name = '-followers-' if self.name == 'followers'
end
alias members group_members
alias members list_members
def add_member(user)
GroupMember.create! group: self, user: user
ListMember.create! list: self, user: user
end
def remove_member(user)
GroupMember.where(group: self, user: user).first!.destroy
ListMember.where(list: self, user: user).first!.destroy
end
end

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true
module Group::TimelineMethods
module List::TimelineMethods
include CursorPaginatable
define_cursor_paginator :cursored_timeline, :timeline
# @return [Array] the groups' timeline
# @return [Array] the lists' timeline
def timeline
Answer.where('user_id in (?)', members.pluck(:user_id)).order(:created_at).reverse_order
end

View File

@ -0,0 +1,4 @@
class ListMember < ApplicationRecord
belongs_to :user
belongs_to :list
end

View File

@ -34,8 +34,8 @@ class User < ApplicationRecord
has_many :reports, dependent: :destroy
has_many :moderation_comments, dependent: :destroy
has_many :moderation_votes, dependent: :destroy
has_many :groups, dependent: :destroy
has_many :group_memberships, class_name: "GroupMember", foreign_key: 'user_id', dependent: :destroy
has_many :lists, dependent: :destroy
has_many :list_memberships, class_name: "ListMember", foreign_key: 'user_id', dependent: :destroy
has_many :subscriptions, dependent: :destroy
@ -110,10 +110,10 @@ class User < ApplicationRecord
friends.include? target_user
end
# @param group [Group]
# @return [Boolean] true if +self+ is a member of +group+
def member_of?(group)
group_memberships.pluck(:group_id).include? group.id
# @param list [List]
# @return [Boolean] true if +self+ is a member of +list+
def member_of?(list)
list_memberships.pluck(:list_id).include? list.id
end
# answers a question

View File

@ -4,6 +4,6 @@
= render 'shared/sidebar'
.col-md-9.col-xs-12.col-sm-8
= render 'layouts/messages'
= render 'tabs/feed', group: @group
= render 'tabs/feed', list: @list
= yield
.d-block.d-sm-none= render 'shared/links'

View File

@ -10,7 +10,7 @@
= render 'tabs/profile', user: @user
= yield
- if user_signed_in?
= render 'modal/group', user: @user
= render 'modal/list', user: @user
- if current_user.mod? && @user != current_user
= render 'modal/privileges', user: @user
= render 'modal/ban', user: @user

View File

@ -8,5 +8,5 @@
%button.btn.btn-default#load-more-btn{ type: :button, data: { last_id: @timeline_last_id } }
= t 'views.actions.load'
- provide(:title, group_title(@group))
- provide(:title, list_title(@list))
- parent_layout 'feed'

View File

@ -9,13 +9,5 @@
.modal-body
%textarea.form-control{ name: 'qb-all-question', placeholder: t('views.placeholder.question') }
.modal-footer
- if current_user.groups.count.positive?
%label
= t 'views.modal.ask.choose'
%select.form-control{ name: 'qb-all-rcpt', autocomplete: :off }
%option{ value: 'followers', selected: true }= t('views.general.follower').pluralize(2)
%optgroup{ label: t('views.group.title').pluralize(2) }
- current_user.groups.each do |group|
%option{ value: "grp:#{group.name}" }= group.display_name
%button.btn.btn-default{ type: :button, data: { dismiss: :modal } }= t 'views.actions.cancel'
%button.btn.btn-primary{ name: 'qb-all-ask', type: :button, data: { loading_text: t('views.modal.ask.loading') } }= t 'views.actions.ask'

View File

@ -1,28 +0,0 @@
.modal.fade#modal-group-memberships{ aria: { hidden: true, labelledby: 'modal-group-memberships-label' }, role: :dialog, tabindex: -1 }
.modal-dialog
.modal-content
.modal-header
%h5.modal-title#modal-group-memberships-label= t 'views.modal.group.title'
%button.close{ data: { dismiss: :modal }, type: :button }
%span{ aria: { hidden: true } } ×
%span.sr-only= t 'views.actions.close'
%div{ role: :tabpanel }
%ul.nav.nav-tabs.mt-1{ role: :tablist }
%li.nav-item{ role: 'presentation' }
%a.nav-link.active{ href: '#grouplist', aria: { controls: 'grouplist' }, data: { toggle: :tab }, role: :tab }
= t 'views.modal.group.tabs.main'
%li.nav-item{ role: 'presentation' }
%a.nav-link{ href: '#create', aria: { controls: 'create' }, data: { toggle: :tab }, role: :tab }
= t 'views.modal.group.tabs.create'
.tab-content
.tab-pane.active{ role: :tabpanel, id: 'grouplist' }
%ul.list-group
- current_user.groups.each do |group|
= render 'modal/group/item', group: group, user: user
.tab-pane{ role: :tabpanel, id: 'create' }
.modal-body
%input.form-control#new-group-name{ type: :text, placeholder: t('views.modal.group.name') }
%button.btn.btn-primary#create-group{ type: :button, data: { user: user.screen_name } }= t('views.modal.group.create')
.modal-footer
%button.btn.btn-primary{ name: 'gm-save', type: :button, data: { dismiss: :modal } }= t 'views.actions.done'

View File

@ -0,0 +1,28 @@
.modal.fade#modal-list-memberships{ aria: { hidden: true, labelledby: 'modal-list-memberships-label' }, role: :dialog, tabindex: -1 }
.modal-dialog
.modal-content
.modal-header
%h5.modal-title#modal-list-memberships-label= t 'views.modal.list.title'
%button.close{ data: { dismiss: :modal }, type: :button }
%span{ aria: { hidden: true } } ×
%span.sr-only= t 'views.actions.close'
%div{ role: :tabpanel }
%ul.nav.nav-tabs.mt-1{ role: :tablist }
%li.nav-item{ role: 'presentation' }
%a.nav-link.active{ href: '#lists-list', aria: { controls: 'lists-list' }, data: { toggle: :tab }, role: :tab }
= t 'views.modal.list.tabs.main'
%li.nav-item{ role: 'presentation' }
%a.nav-link{ href: '#create', aria: { controls: 'create' }, data: { toggle: :tab }, role: :tab }
= t 'views.modal.list.tabs.create'
.tab-content
.tab-pane.active{ role: :tabpanel, id: 'lists-list' }
%ul.list-group
- current_user.lists.each do |list|
= render 'modal/list/item', list: list, user: user
.tab-pane{ role: :tabpanel, id: 'create' }
.modal-body
%input.form-control#new-list-name{ type: :text, placeholder: t('views.modal.list.name') }
%button.btn.btn-primary#create-list{ type: :button, data: { user: user.screen_name } }= t('views.modal.list.create')
.modal-footer
%button.btn.btn-primary{ name: 'gm-save', type: :button, data: { dismiss: :modal } }= t 'views.actions.done'

View File

@ -7,7 +7,7 @@
%button.close{ data: { dismiss: :modal }, type: :button }
%span{ aria: { hidden: true } } ×
%span.sr-only= t 'views.actions.close'
%ul.list-group.groups--list
%ul.list-group
- if current_user.has_role?(:administrator)
= render 'modal/privileges/item', privilege: 'moderator', description: t('views.modal.privilege.moderator'), user: user
= render 'modal/privileges/item', privilege: 'admin', description: t('views.modal.privilege.admin'), user: user

View File

@ -1,18 +0,0 @@
%li.list-group-item{ id: "group-#{group.name}" }
.media
.pull-left
.custom-control.custom-checkbox
%input.custom-control-input{ type: :checkbox,
id: "groupCheck#{group.id}",
name: 'gm-group-check',
data: { group: group.name, user: user.screen_name }, checked: user.member_of?(group), autocomplete: :off }
%label.custom-control-label{ for: "groupCheck#{group.id}" }
.media-body
.list-group-item-heading= group.display_name
.list-group-item-text.text-muted
%span{ id: "#{group.name}-members" }= group.members.count
= t 'views.modal.group.members'
·
%a.text-danger#delete-group{ href: '#', data: { group: group.name } }
%i.fa.fa-close
= t 'views.actions.delete'

View File

@ -0,0 +1,18 @@
%li.list-group-item{ id: "list-#{list.name}" }
.media
.pull-left
.custom-control.custom-checkbox
%input.custom-control-input{ type: :checkbox,
id: "listCheck#{list.id}",
name: 'gm-list-check',
data: { list: list.name, user: user.screen_name }, checked: user.member_of?(list), autocomplete: :off }
%label.custom-control-label{ for: "listCheck#{list.id}" }
.media-body
.list-group-item-heading= list.display_name
.list-group-item-text.text-muted
%span{ id: "#{list.name}-members" }= list.members.count
= t 'views.modal.list.members'
·
%a.text-danger#delete-list{ href: '#', data: { list: list.name } }
%i.fa.fa-close
= t 'views.actions.delete'

View File

@ -13,10 +13,10 @@
%ul.nav.navbar-nav
- unless @user.nil?
- unless @user == current_user
%li.nav-item.d-none.d-sm-block{ data: { toggle: 'tooltip', placement: 'bottom' }, title: t('views.actions.group') }
%a.nav-link{ href: '#', data: { target: '#modal-group-memberships', toggle: :modal } }
%i.fa.fa-users.hidden-xs
%span.d-none.d-sm-inline.d-md-none= t('views.actions.group')
%li.nav-item.d-none.d-sm-block{ data: { toggle: 'tooltip', placement: 'bottom' }, title: t('views.actions.list') }
%a.nav-link{ href: '#', data: { target: '#modal-list-memberships', toggle: :modal } }
%i.fa.fa-list.hidden-xs
%span.d-none.d-sm-inline.d-md-none= t('views.actions.list')
= render 'navigation/main/notifications'
%li.nav-item.d-none.d-sm-block{ data: { toggle: 'tooltip', placement: 'bottom' }, title: t('views.actions.ask_question') }
%a.nav-link{ href: '#', name: 'toggle-all-ask', data: { target: '#modal-ask-followers', toggle: :modal } }

View File

@ -8,13 +8,13 @@
= current_user.display_name
.profile__screen-name
= current_user.screen_name
- unless @group.nil?
- unless @list.nil?
.card
.card-header= t('views.group.members')
.card-header= t('views.list.members')
.card-body
- if @group.members.empty?
%p.text-muted No members yet!
- @group.members.each do |member|
- if @list.members.empty?
%p.text-muted No members yet.
- @list.members.each do |member|
%a{ href: show_user_profile_path(member.user.screen_name), title: member.user.screen_name, data: { toggle: :tooltip, placement: :top } }
%img.avatar-xs{ src: member.user.profile_picture.url(:medium) }

View File

@ -65,11 +65,10 @@
.row
.col-md-4.col-sm-4.col-xs-12
%h3
%i.fa.fa-fw.fa-users.text-primary
Groups
%i.fa.fa-fw.fa-list.text-primary
Lists
%p
Want to ask a question to a specific set of people? No problem!
Set up unique groups of users and send them questions apart from everyone else.
Want to keep an oversight of things? Organise users in Lists to create timelines of their answers.
.col-md-4.col-sm-4.col-xs-12
%h3
%i.fa.fa-fw.fa-image.text-primary

View File

@ -3,23 +3,22 @@
= list_group_item t('views.general.timeline'), root_path
- if APP_CONFIG.dig(:features, :public, :enabled)
= list_group_item t('views.general.public'), public_timeline_path
.list-group-item.list-group-item-action.dropdown{ class: group ? 'active' : '' }
.list-group-item.list-group-item-action.dropdown{ class: list ? 'active' : '' }
%a.dropdown-toggle{ type: :button, data: { toggle: :dropdown }, aria: { haspopup: true, expanded: false } }
- if group
= group.display_name
- if list
= list.display_name
- else
Groups
Lists
.dropdown-menu
- if current_user.groups.empty?
- if current_user.lists.empty?
.p-3
%p Looks like you don't have any groups yet.
%p Looks like you don't have any lists yet.
%p
You can create groups and add users to them using the
%i.fa.fa-fw.fa-users
You can create lists and add users to them using the
%i.fa.fa-fw.fa-list
icon in the navigation on user profiles that are not yours.
%p.mb-0
Once you have done that, the groups will be listed here and
when selected, you'll get a timeline view of all users from
that group.
- current_user.groups.each do |group|
%a.dropdown-item{ href: group_timeline_path(group.name) }= group.display_name
Once you have done that, the lists will be shown here.
When you select a list you'll get a timeline view of all users within that list.
- current_user.lists.each do |list|
%a.dropdown-item{ href: list_timeline_path(list.name) }= list.display_name

View File

@ -14,9 +14,9 @@
= t 'views.actions.title'
%span.caret
.dropdown-menu
%a.dropdown-item.d-block.d-sm-none{ href: '#', data: { target: '#modal-group-memberships', toggle: :modal } }
%i.fa.fa-users
= t 'views.actions.group'
%a.dropdown-item.d-block.d-sm-none{ href: '#', data: { target: '#modal-list-memberships', toggle: :modal } }
%i.fa.fa-list
= t 'views.actions.list'
%a.dropdown-item{ href: '#', data: { action: 'report-user', target: user.screen_name } }
%i.fa.fa-exclamation-triangle
= t 'views.actions.report'

View File

@ -1,9 +0,0 @@
%ul
- @groups.each do |group|
%li
- if group.private?
%i.fa.fa-lock
= group.display_name
- provide(:title, user_title(@user, 'groups'))
- parent_layout 'user/profile'

View File

@ -0,0 +1,9 @@
%ul
- @lists.each do |list|
%li
- if list.private?
%i.fa.fa-lock
= list.display_name
- provide(:title, user_title(@user, 'lists'))
- parent_layout 'user/profile'

View File

@ -1,28 +1,20 @@
# frozen_string_literal: true
class QuestionWorker
include Sidekiq::Worker
sidekiq_options queue: :question, retry: false
# @param rcpt [String] recipient
# @param user_id [Integer] user id passed from Devise
# @param question_id [Integer] newly created question id
def perform(rcpt, user_id, question_id)
begin
user = User.find(user_id)
if rcpt == 'followers'
user.followers.each do |f|
Inbox.create(user_id: f.id, question_id: question_id, new: true)
end
elsif rcpt.start_with? 'grp:'
user.groups.find_by_name!(rcpt.sub 'grp:', '').members.each do |m|
Inbox.create(user_id: m.user.id, question_id: question_id, new: true)
end
else
logger.info "unknown rcpt #{rcpt}"
end
rescue => e
logger.info "failed to ask question: #{e.message}"
NewRelic::Agent.notice_error(e)
def perform(user_id, question_id)
user = User.find(user_id)
user.followers.each do |f|
Inbox.create(user_id: f.id, question_id: question_id, new: true)
end
rescue StandardError => e
logger.info "failed to ask question: #{e.message}"
NewRelic::Agent.notice_error(e)
end
end

View File

@ -30,8 +30,8 @@ RailsAdmin.config do |config|
config.included_models = %w[
Answer
Comment
Group
GroupMember
List
ListMember
Inbox
Notification
Question

View File

@ -14,7 +14,7 @@ cs:
fail:
subscribe: "Nepodařilo se přihlásit k odběru na odpovědi."
unsubscribe: "Nepodařilo se zrušit odběr na odpovědi."
group:
list:
confirm:
title: "Opravdu chcete smazat tuto skupinu?"
text: "Nebudete tuto skupinu moci obnovit."
@ -95,7 +95,7 @@ cs:
destroy:
fail: "Nesledujete tohoto uživatele."
okay: "Používatel úspěšně odsledován."
group:
list:
create:
noname: "Prosím pojmenujte tuto skupinu."
toolong: "Jméno skupiny příliš dlhé (max. 30 znaků)"
@ -235,7 +235,7 @@ cs:
terms: "Podmínky služby"
privacy: "Ochrana osobních údajů"
about: "O něm"
group:
list:
title: "Skupina"
members: "Členové"
actions:
@ -254,7 +254,7 @@ cs:
load: "Načíst víc"
follow: "Sledovat"
unfollow: "Nesledovat"
group: "Spravovat skupinové členství"
list: "Spravovat skupinové členství"
ban: "Správa Banů"
privilege: "Zkontrolovat výsady %{user}"
save: "Uložit změny"
@ -424,7 +424,7 @@ cs:
permanent: "Permanentně?"
reason: "Důvod"
hammertime: "Hammer Time"
group:
list:
title: "Spravovat skupinové členství"
tabs:
main: "Skupiny"

View File

@ -14,10 +14,10 @@ de:
fail:
subscribe: "Das Abonnieren der Antwort schlug fehl."
unsubscribe: "Das Deabonnieren der Antwort schlug fehl."
group:
list:
confirm:
title: "Diese Gruppe wirklich löschen?"
text: "Es wird nicht möglich sein, diese Gruppe wieder zu erhalten."
title: "Diese Liste wirklich löschen?"
text: "Es wird nicht möglich sein, diese Liste wieder zu erhalten."
destroy_question:
confirm:
title: "Bist du sicher?"
@ -97,20 +97,20 @@ de:
destroy:
fail: "Du folgst diesem Benutzer nicht."
okay: "Erfolgreich Benutzer entfolgt."
group:
list:
create:
noname: "Bitte gib dieser Gruppe einen Namen."
toolong: "Gruppenname zu lang (maximal 30 Zeichen)"
noname: "Bitte gib dieser Liste einen Namen."
toolong: "Listenname zu lang (maximal 30 Zeichen)"
notfound: "Konnte Benutzer nicht finden."
exists: "Gruppe existiert bereits."
okay: "Gruppe erfolgreich erstellt."
exists: "Liste existiert bereits."
okay: "Liste erfolgreich erstellt."
destroy:
notfound: "Konnte Gruppe nicht finden."
okay: "Gruppe erfolgreich gelöscht."
notfound: "Konnte Liste nicht finden."
okay: "Liste erfolgreich gelöscht."
membership:
notfound: "Gruppe nicht gefunden."
add: "Benutzer erfolgreich zur Gruppe hinzugefügt."
remove: "Benutzer erfolgreich aus Gruppe entfernt."
notfound: "Liste nicht gefunden."
add: "Benutzer erfolgreich zur Liste hinzugefügt."
remove: "Benutzer erfolgreich aus Liste entfernt."
inbox:
create:
okay: "Erfolgreich neue Frage hinzugefügt."
@ -152,7 +152,7 @@ de:
okay: "Frage erfolgreich gelöscht."
create:
rec_inv: "Deine Frage ist zu lang."
not_found: "Gruppe nicht gefunden"
not_found: "Liste nicht gefunden"
okay: "Frage erfolgreich gestellt."
report:
create:
@ -237,8 +237,8 @@ de:
terms: "Allgemeine Nutzungsbedingungen"
privacy: Datenschutz
about: Über
group:
title: Gruppe
list:
title: Liste
members: Mitglieder
actions:
title: Aktionen
@ -256,7 +256,7 @@ de:
load: Mehr...
follow: Folgen
unfollow: Entfolgen
group: "Verwalten von Gruppenmitgliedschaften"
list: "Verwalten von Listenmitgliedschaften"
ban: Bann-Steuerung
privilege: "Prüfe %{user}'s Privilegien"
save: "Änderungen speichern"
@ -420,7 +420,7 @@ de:
modal:
ask:
title: "Frage deine Follower"
choose: "Gruppe:"
choose: "Liste:"
loading: Fragen…
bancontrol:
title: Bannkontrollzentrum
@ -428,13 +428,13 @@ de:
permanent: "Für immer?"
reason: Grund
hammertime: Hammerzeit!
group:
title: "Gruppenzugehörigkeit verwalten"
list:
title: "Listenzugehörigkeit verwalten"
tabs:
main: Gruppen
create: "Neue Gruppe erstellen"
create: "Gruppe erstellen"
name: Gruppenname
main: Listen
create: "Neue Liste erstellen"
create: "Liste erstellen"
name: Listenname
members: Mitglieder
privilege:
blogger: "Der Benutzer erhält dieses Privileg wenn er etwas (nettes) über Retrospring gebloggt hat."

View File

@ -13,10 +13,10 @@ en:
fail:
subscribe: "Failed to subscribe to answer."
unsubscribe: "Failed to unsubscribe from answer."
group:
list:
confirm:
title: "Really delete this group?"
text: "You will not be able to recover this group."
title: "Really delete this list?"
text: "You will not be able to recover this list."
destroy_question:
confirm:
title: "Are you sure?"
@ -94,20 +94,20 @@ en:
destroy:
fail: "You are not following that user."
okay: "Successfully unfollowed user."
group:
list:
create:
noname: "Please give that group a name."
toolong: "Group name too long (30 characters max.)"
noname: "Please give that list a name."
toolong: "List name too long (30 characters max.)"
notfound: "Could not find user."
exists: "Group already exists."
okay: "Successfully created group."
exists: "List already exists."
okay: "Successfully created list."
destroy:
notfound: "Could not find group."
okay: "Successfully deleted group."
notfound: "Could not find list."
okay: "Successfully deleted list."
membership:
notfound: "Group not found."
add: "Successfully added user to group."
remove: "Successfully removed user from group."
notfound: "List not found."
add: "Successfully added user to list."
remove: "Successfully removed user from list."
inbox:
create:
okay: "Successfully added new question."
@ -149,7 +149,7 @@ en:
okay: "Successfully deleted question."
create:
rec_inv: "Your question is too long."
not_found: "Group not found"
not_found: "List not found"
okay: "Question asked successfully."
report:
create:
@ -237,8 +237,8 @@ en:
terms: "Terms of Service"
privacy: "Privacy Policy"
about: "About"
group:
title: "Group"
list:
title: "List"
members: "Members"
actions:
title: "Actions"
@ -256,7 +256,7 @@ en:
load: "Load more"
follow: "Follow"
unfollow: "Unfollow"
group: "Manage group memberships"
list: "Manage list memberships"
ban: "Ban Control"
privilege: "Check %{user}'s privileges"
save: "Save changes"
@ -418,7 +418,7 @@ en:
modal:
ask:
title: "Ask your followers"
choose: "Choose group:"
choose: "Choose list:"
loading: "Asking..."
bancontrol:
title: "Ban Control Center"
@ -426,13 +426,13 @@ en:
permanent: "Permanently?"
reason: "Reason"
hammertime: "Hammer Time"
group:
title: "Manage group memberships"
list:
title: "Manage list memberships"
tabs:
main: "Groups"
create: "Create new group"
create: "Create group"
name: "Group name"
main: "Lists"
create: "Create new list"
create: "Create list"
name: "List name"
members: "members"
privilege:
moderator: "Someone trustworthy enough to help managing reports."

View File

@ -13,10 +13,10 @@ en_dizzle:
fail:
subscribe: "Failed ta subscribe ta answer."
unsubscribe: "Failed ta unsubscribe from answer."
group:
list:
confirm:
title: "Straight-Up delete dis group?"
text: "Yo ass aint gonna be able ta recover dis group."
title: "Straight-Up delete dis list?"
text: "Yo ass aint gonna be able ta recover dis list."
destroy_question:
confirm:
title: "Is you sure?"
@ -94,20 +94,20 @@ en_dizzle:
destroy:
fail: "Yo ass aint followin dat user."
okay: "Successfully unfollowed user."
group:
list:
create:
noname: "Please give dat crew a name."
toolong: "Group name too long (30 charactas max.)"
toolong: "List name too long (30 charactas max.)"
notfound: "Could not find user."
exists: "Group already exists."
okay: "Successfully pimped group."
exists: "List already exists."
okay: "Successfully pimped list."
destroy:
notfound: "Could not find group."
okay: "Successfully deleted group."
notfound: "Could not find list."
okay: "Successfully deleted list."
membership:
notfound: "Group not found."
add: "Successfully added user ta group."
remove: "Successfully removed user from group."
notfound: "List not found."
add: "Successfully added user ta list."
remove: "Successfully removed user from list."
inbox:
create:
okay: "Successfully added freshly smoked up question."
@ -149,7 +149,7 @@ en_dizzle:
okay: "Successfully deleted question."
create:
rec_inv: "Yo crazy-ass question is too long."
not_found: "Group not found"
not_found: "List not found"
okay: "Question asked successfully."
report:
create:
@ -235,7 +235,7 @@ en_dizzle:
terms: "Tha termz"
privacy: "Shit that no one ain't read"
about: "'bout"
group:
list:
title: "Fam"
members: "Peepz"
actions:
@ -254,7 +254,7 @@ en_dizzle:
load: "Load sum more"
follow: "Go check on tha homeboy"
unfollow: "Stop checkin' on tha homeboy"
group: "Manage crew memberships"
list: "Manage crew memberships"
ban: "Banish that fucker"
privilege: "Peep %{user}z privileges"
save: "Save chizzles"
@ -424,7 +424,7 @@ en_dizzle:
permanent: "Fo' all the timez?"
reason: "Reason"
hammertime: "Hammer Time"
group:
list:
title: "Manage crew memberships"
tabs:
main: "Crews"

View File

@ -13,7 +13,7 @@ en_pirate:
fail:
subscribe: "Failed to subscribe to answer."
unsubscribe: "Failed to unsubscribe from answer."
group:
list:
confirm:
title: "D' ye wish to send this crew t' Davy Jones' Locker?"
text: "Ye gunna not be able to recover 'tis crew."
@ -94,7 +94,7 @@ en_pirate:
destroy:
fail: "Ye not be spyin' on that user."
okay: "No longer spyin' on user."
group:
list:
create:
noname: "Please gift that crew a moniker."
toolong: "Crew moniker too long (30 characters max.)"
@ -235,7 +235,7 @@ en_pirate:
terms: "Terms of ye Service"
privacy: "Pirat-y Policy"
about: "About"
group:
list:
title: "Crew"
members: "Members"
actions:
@ -254,7 +254,7 @@ en_pirate:
load: "Load more"
follow: "Spy"
unfollow: "Un-Spy"
group: "Manage Crew memberships"
list: "Manage Crew memberships"
ban: "Ban Control"
privilege: "Check %{user}'s privileges"
save: "Save changes"
@ -424,7 +424,7 @@ en_pirate:
permanent: "Permanently?"
reason: "Reason"
hammertime: "Ye olde Hammer Time!"
group:
list:
title: "Manage crew memberships"
tabs:
main: "Crews"

View File

@ -14,9 +14,9 @@ fr:
fail:
subscribe: "Échec de l'abonnement à la réponse."
unsubscribe: "Échec du désabonnement à la réponse."
group:
list:
confirm:
title: "Voulez-vous vraiment supprimer ce groupe ?"
title: "Voulez-vous vraiment supprimer cette liste ?"
text: "Vous ne pourrez pas le récupérer."
destroy_question:
confirm:
@ -97,18 +97,18 @@ fr:
destroy:
fail: "Vous ne suivez pas cet utilisateur."
okay: "Vous ne suivez plus cet utilisateur."
group:
list:
create:
noname: "Veuillez donner un nom à ce groupe."
toolong: "Le nom du groupe est trop long (30 caractères max.)"
noname: "Veuillez donner un nom à cette liste."
toolong: "Le nom de la liste est trop long (30 caractères max.)"
notfound: "L'utilisateur est introuvable."
exists: "Ce groupe existe déjà."
okay: "Le groupe a été créé avec succès."
exists: "Cette liste existe déjà."
okay: "La liste a été créé avec succès."
destroy:
notfound: "Le groupe est introuvable."
okay: "Le groupe a été supprimé avec succès."
notfound: "La liste est introuvable."
okay: "La liste a été supprimé avec succès."
membership:
notfound: "Le groupe est introuvable."
notfound: "La liste est introuvable."
add: "L'utilisateur a été ajouté avec succès."
remove: "L'utilisateur a été enlevé avec succès."
inbox:
@ -152,7 +152,7 @@ fr:
okay: "La question a été supprimée avec succès."
create:
rec_inv: "Votre question est trop longue."
not_found: "Le groupe est introuvable."
not_found: "La liste est introuvable."
okay: "La question a été posée avec succès."
report:
create:
@ -237,8 +237,8 @@ fr:
terms: "Conditions générales d'utilisation"
privacy: "Politique de confidentialité"
about: "À propos"
group:
title: "Groupe"
list:
title: "Liste"
members: "Membres"
actions:
title: "Actions"
@ -256,7 +256,7 @@ fr:
load: "Charger plus"
follow: "Suivre"
unfollow: "Ne plus suivre"
group: "Gérer l'appartenance à un groupe"
list: "Gérer l'appartenance à une liste"
ban: "Contrôle des bannissements"
privilege: "Vérifier les privilèges de %{user}"
save: "Enregistrer les modifications"
@ -418,7 +418,7 @@ fr:
modal:
ask:
title: "Poser une question à vos abonnés"
choose: "Choisir un groupe :"
choose: "Choisir une liste :"
loading: "Envoi..."
bancontrol:
title: "Centre de contrôle des bannissements"
@ -426,13 +426,13 @@ fr:
permanent: "Définitivement ?"
reason: "Raison"
hammertime: "Que le Marteau s'abatte"
group:
title: "Gérer l'appartenance à un groupe"
list:
title: "Gérer l'appartenance à une liste"
tabs:
main: "Groupes"
create: "Créer un nouveau groupe"
main: "Listes"
create: "Créer une nouvelle liste"
create: "Créer"
name: "Nom du groupe"
name: "Nom de la liste"
members: "membres"
privilege:
blogger: "L'utilisateur obtient ce titre s'il a posté un article (agréable) à propos de Retrospring."

View File

@ -13,7 +13,7 @@ it:
fail:
subscribe: "Impossibile iscriversi alla risposta."
unsubscribe: "Impossibile disiscriversi dalla risposta."
group:
list:
confirm:
title: "Vuoi davvero eliminare questo gruppo?"
text: "Non sarai più in grado di recuperare questo gruppo."
@ -94,7 +94,7 @@ it:
destroy:
fail: "Non stai seguendo quell'utente."
okay: "Non stai più seguendo quell'utente."
group:
list:
create:
noname: "Per favore, dai un nome a quel gruppo."
toolong: "Nome del gruppo troppo grande (massimo 30 caratteri)"
@ -236,7 +236,7 @@ it:
terms: "Termini di Servizio"
privacy: "Normativa sulla privacy"
about: "Informazioni"
group:
list:
title: "Gruppo"
members: "Membri"
actions:
@ -255,7 +255,7 @@ it:
load: "Carica altro"
follow: "Segui"
unfollow: "Non seguire più"
group: "Impostazioni sui gruppi"
list: "Impostazioni sui gruppi"
ban: "Controlla Ban"
privilege: "Controlla i privilegi dell'%{user}"
save: "Salva le impostazioni"
@ -425,7 +425,7 @@ it:
permanent: Permanentemente?
reason: "Motivo"
hammertime: "Tempo del ban"
group:
list:
title: "Gestisci appartenenza a gruppi"
tabs:
main: "Gruppi"

View File

@ -13,7 +13,7 @@ ja:
fail:
subscribe: 回答の購読に失敗しました。
unsubscribe: 回答の購読の解除に失敗しました。
group:
list:
confirm:
title: このグループを削除してもよろしいですか?
text: このグループを復元することはできなくなります。
@ -96,7 +96,7 @@ ja:
destroy:
fail: このユーザーをフォローしていません。
okay: フォローの解除に成功しました。
group:
list:
create:
noname: グループに名前をつけてください。
toolong: グループ名が長過ぎます(最大30文字)
@ -236,7 +236,7 @@ ja:
terms: 利用規約
privacy: プライバシーポリシー
about: Retrospringについて
group:
list:
title: グループ
members: メンバー
actions:
@ -255,7 +255,7 @@ ja:
load: つづきを読み込む
follow: フォロー
unfollow: フォロー解除
group: グループのメンバーを管理
list: グループのメンバーを管理
ban: 利用停止管理
privilege: "%{user}の権限を確認する"
save: 変更を保存する
@ -425,7 +425,7 @@ ja:
permanent: 永久的に追放しますか?
reason: 理由
hammertime: 利用停止時間
group:
list:
title: グループのメンバーを管理
tabs:
main: グループ

View File

@ -102,16 +102,16 @@ Rails.application.routes.draw do
match '/create_comment', to: 'comment#create', via: :post, as: :create_comment
match '/destroy_comment', to: 'comment#destroy', via: :post, as: :destroy_comment
match '/report', to: 'report#create', via: :post, as: :report
match '/create_group', to: 'group#create', via: :post, as: :create_group
match '/destroy_group', to: 'group#destroy', via: :post, as: :destroy_group
match '/group_membership', to: 'group#membership', via: :post, as: :group_membership
match '/create_list', to: 'list#create', via: :post, as: :create_list
match '/destroy_list', to: 'list#destroy', via: :post, as: :destroy_list
match '/list_membership', to: 'list#membership', via: :post, as: :list_membership
match '/subscribe', to: 'subscription#subscribe', via: :post, as: :subscribe_answer
match '/unsubscribe', to: 'subscription#unsubscribe', via: :post, as: :unsubscribe_answer
end
match '/discover', to: 'discover#index', via: :get, as: :discover
match '/public', to: 'public#index', via: :get, as: :public_timeline if APP_CONFIG.dig(:features, :public, :enabled)
match '/group/:group_name', to: 'group#index', via: :get, as: :group_timeline
match '/list/:list_name', to: 'list#index', via: :get, as: :list_timeline
match '/notifications(/:type)', to: 'notifications#index', via: :get, as: :notifications, defaults: {type: 'new'}
@ -129,7 +129,7 @@ Rails.application.routes.draw do
match '/:username/q/:id', to: 'question#show', via: 'get', as: :show_user_question
match '/:username/followers(/p/:page)', to: 'user#followers', via: 'get', as: :show_user_followers, defaults: {page: 1}
match '/:username/friends(/p/:page)', to: 'user#friends', via: 'get', as: :show_user_friends, defaults: {page: 1}
match '/:username/groups(/p/:page)', to: 'user#groups', via: 'get', as: :show_user_groups, defaults: {page: 1}
match '/:username/lists(/p/:page)', to: 'user#lists', via: 'get', as: :show_user_lists, defaults: {page: 1}
match '/:username/questions(/p/:page)', to: 'user#questions', via: 'get', as: :show_user_questions, defaults: {page: 1}
puts 'processing time of routes.rb: ' + "#{(Time.now - start).round(3).to_s.ljust(5, '0')}s".light_green

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class RenameGroupsToLists < ActiveRecord::Migration[5.2]
def change
say "Renaming group-related tables"
rename_table :groups, :lists
rename_table :group_members, :list_members
say "Renaming group-related columns"
rename_column :list_members, :group_id, :list_id
say "Cleaning up unused/already covered indexes"
# Rails already renames the indexes for us when we rename tables or columns.
# Neat!
remove_index :list_members, name: "index_list_members_on_list_id"
remove_index :list_members, name: "index_list_members_on_user_id"
remove_index :lists, name: "index_lists_on_name"
remove_index :lists, name: "index_lists_on_user_id"
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_05_17_192431) do
ActiveRecord::Schema.define(version: 2020_05_25_145144) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -60,28 +60,6 @@ ActiveRecord::Schema.define(version: 2020_05_17_192431) do
t.index ["user_id", "created_at"], name: "index_comments_on_user_id_and_created_at"
end
create_table "group_members", id: :serial, force: :cascade do |t|
t.integer "group_id", null: false
t.integer "user_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.index ["group_id", "user_id"], name: "index_group_members_on_group_id_and_user_id", unique: true
t.index ["group_id"], name: "index_group_members_on_group_id"
t.index ["user_id"], name: "index_group_members_on_user_id"
end
create_table "groups", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.string "name"
t.string "display_name"
t.boolean "private", default: true
t.datetime "created_at"
t.datetime "updated_at"
t.index ["name"], name: "index_groups_on_name"
t.index ["user_id", "name"], name: "index_groups_on_user_id_and_name", unique: true
t.index ["user_id"], name: "index_groups_on_user_id"
end
create_table "inboxes", id: :serial, force: :cascade do |t|
t.integer "user_id"
t.integer "question_id"
@ -91,6 +69,24 @@ ActiveRecord::Schema.define(version: 2020_05_17_192431) do
t.index ["user_id"], name: "index_inboxes_on_user_id"
end
create_table "list_members", id: :serial, force: :cascade do |t|
t.integer "list_id", null: false
t.integer "user_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.index ["list_id", "user_id"], name: "index_list_members_on_list_id_and_user_id", unique: true
end
create_table "lists", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.string "name"
t.string "display_name"
t.boolean "private", default: true
t.datetime "created_at"
t.datetime "updated_at"
t.index ["user_id", "name"], name: "index_lists_on_user_id_and_name", unique: true
end
create_table "moderation_comments", id: :serial, force: :cascade do |t|
t.integer "report_id"
t.integer "user_id"

View File

@ -6,7 +6,7 @@ Upon creation of a Retrospring account, some personal information such as your
Public information like a short biography, a link to your website, your location or a profile picture can be provided after registration. This information is viewable by any visitor of the site, registered or unregistered, and therefore should not contain any critical information about you or someone else.
#### Answers and Questions
Our Service primarily is designed to ask your friends and other people from around the world questions or answer them. Most of the information you provide us is information you are asking us to make public. This information does not only include the questions, answers, comments and smiles, but also the groups you create, the people you follow and other bits of information that result from using our Service.
Our Service primarily is designed to ask your friends and other people from around the world questions or answer them. Most of the information you provide us is information you are asking us to make public. This information does not only include the questions, answers, comments and smiles, but also the lists you create, the people you follow and other bits of information that result from using our Service.
#### Cookies
Like many websites, we use cookies and similar technologies to collect additional website usage data and to improve our Services. A cookie is a small data file that is transferred to your computer's hard disk. Retrospring uses persistant cookies to improve the users browsing experience and to to better understand how you interact with our Services. Most Internet browsers automatically accept cookies. You can instruct your browser, by changing its settings, to stop accepting cookies or to prompt you before accepting a cookie from the websites you visit. However, the Services may not function properly if you disable cookies.
#### Non-personal Identification Information

View File

@ -2,7 +2,7 @@
require "rails_helper"
describe Ajax::GroupController, :ajax_controller, type: :controller do
describe Ajax::ListController, :ajax_controller, type: :controller do
let(:target_user) { FactoryBot.create(:user) }
describe "#create" do
@ -29,8 +29,8 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
before(:each) { sign_in(user) }
it "creates the group" do
expect { subject }.to(change { user.groups.count }.by(1))
it "creates the list" do
expect { subject }.to(change { user.lists.count }.by(1))
end
include_examples "returns the expected response"
@ -45,8 +45,8 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
}
end
it "does not create the group" do
expect { subject }.not_to(change { user.groups.count })
it "does not create the list" do
expect { subject }.not_to(change { user.lists.count })
end
include_examples "returns the expected response"
@ -62,14 +62,14 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
}
end
it "does not create the group" do
expect { subject }.not_to(change { user.groups.count })
it "does not create the list" do
expect { subject }.not_to(change { user.lists.count })
end
include_examples "returns the expected response"
end
context "when group name is invalid for reasons" do
context "when list name is invalid for reasons" do
let(:name) { "\u{1f43e}" }
let(:expected_response) do
{
@ -79,14 +79,14 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
}
end
it "does not create the group" do
expect { subject }.not_to(change { user.groups.count })
it "does not create the list" do
expect { subject }.not_to(change { user.lists.count })
end
include_examples "returns the expected response"
end
context "when group already exists" do
context "when list already exists" do
before(:each) { post(:create, params: params) }
let(:expected_response) do
{
@ -96,20 +96,20 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
}
end
it "does not create the group" do
expect { subject }.not_to(change { user.groups.count })
it "does not create the list" do
expect { subject }.not_to(change { user.lists.count })
end
include_examples "returns the expected response"
end
context "when someone else created a group with the same name" do
context "when someone else created a list with the same name" do
before(:each) do
FactoryBot.create(:group, user: target_user, display_name: name)
FactoryBot.create(:list, user: target_user, display_name: name)
end
it "creates the group" do
expect { subject }.to(change { user.groups.count }.by(1))
it "creates the list" do
expect { subject }.to(change { user.lists.count }.by(1))
end
include_examples "returns the expected response"
@ -131,11 +131,11 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
describe "#destroy" do
let(:name) { "I signori della gallassia" }
let(:group) { FactoryBot.create(:group, user: user, display_name: name) }
let(:group_param) { group.name }
let(:list) { FactoryBot.create(:list, user: user, display_name: name) }
let(:list_param) { list.name }
let(:params) do
{
"group" => group_param
"list" => list_param
}
end
@ -152,15 +152,15 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
before(:each) { sign_in(user) }
it "deletes the group" do
group
expect { subject }.to(change { user.groups.count }.by(-1))
it "deletes the list" do
list
expect { subject }.to(change { user.lists.count }.by(-1))
end
include_examples "returns the expected response"
context "when group param is missing" do
let(:group_param) { "" }
context "when list param is missing" do
let(:list_param) { "" }
let(:expected_response) do
{
"success" => false,
@ -169,15 +169,15 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
}
end
it "does not delete the group" do
expect { subject }.not_to(change { user.groups.count })
it "does not delete the list" do
expect { subject }.not_to(change { user.lists.count })
end
include_examples "returns the expected response"
end
context "when group does not exist" do
let(:group_param) { "the-foobars-and-the-dingdongs" }
context "when list does not exist" do
let(:list_param) { "the-foobars-and-the-dingdongs" }
let(:expected_response) do
{
"success" => false,
@ -186,25 +186,25 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
}
end
it "does not delete the group" do
expect { subject }.not_to(change { user.groups.count })
it "does not delete the list" do
expect { subject }.not_to(change { user.lists.count })
end
include_examples "returns the expected response"
end
context "when someone else created a group with the same name" do
context "when someone else created a list with the same name" do
before(:each) do
group
FactoryBot.create(:group, user: target_user, display_name: name)
list
FactoryBot.create(:list, user: target_user, display_name: name)
end
it "deletes the group" do
expect { subject }.to(change { user.groups.count }.by(-1))
it "deletes the list" do
expect { subject }.to(change { user.lists.count }.by(-1))
end
it "does not delete the other users' group" do
expect { subject }.not_to(change { target_user.groups.count })
it "does not delete the other users' list" do
expect { subject }.not_to(change { target_user.lists.count })
end
include_examples "returns the expected response"
@ -227,12 +227,12 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
describe "#membership" do
let(:name) { "The Agency" }
let(:members) { [] }
let(:group) { FactoryBot.create(:group, user: user, display_name: name, members: members) }
let(:group_param) { group.name }
let(:list) { FactoryBot.create(:list, user: user, display_name: name, members: members) }
let(:list_param) { list.name }
let(:target_user_param) { target_user.screen_name }
let(:params) do
{
"group" => group_param,
"list" => list_param,
"user" => target_user_param,
"add" => add_param
}
@ -257,17 +257,17 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
let(:expected_checked) { false }
it "does not do anything" do
expect { subject }.not_to(change { group.members })
expect(group.members.map { |gm| gm.user.id }.sort ).to eq([])
expect { subject }.not_to(change { list.members })
expect(list.members.map { |gm| gm.user.id }.sort ).to eq([])
end
include_examples "returns the expected response"
context "when the user was already added to the group" do
context "when the user was already added to the list" do
let(:members) { [target_user] }
it "removes the user from the group" do
expect { subject }.to(change { group.reload.members.map { |gm| gm.user.id }.sort }.from([target_user.id]).to([]))
it "removes the user from the list" do
expect { subject }.to(change { list.reload.members.map { |gm| gm.user.id }.sort }.from([target_user.id]).to([]))
end
include_examples "returns the expected response"
@ -278,26 +278,26 @@ describe Ajax::GroupController, :ajax_controller, type: :controller do
let(:add_param) { "true" }
let(:expected_checked) { true }
it "adds the user to the group" do
expect { subject }.to(change { group.reload.members.map { |gm| gm.user.id }.sort }.from([]).to([target_user.id]))
it "adds the user to the list" do
expect { subject }.to(change { list.reload.members.map { |gm| gm.user.id }.sort }.from([]).to([target_user.id]))
end
include_examples "returns the expected response"
context "when the user was already added to the group" do
context "when the user was already added to the list" do
let(:members) { [target_user] }
it "does not add the user to the group again" do
expect { subject }.not_to(change { group.members })
expect(group.members.map { |gm| gm.user.id }.sort ).to eq([target_user.id])
it "does not add the user to the list again" do
expect { subject }.not_to(change { list.members })
expect(list.members.map { |gm| gm.user.id }.sort ).to eq([target_user.id])
end
include_examples "returns the expected response"
end
end
context "when group does not exist" do
let(:group_param) { "the-good-agency" }
context "when list does not exist" do
let(:list_param) { "the-good-agency" }
let(:add_param) { "add" }
let(:expected_response) do
{

View File

@ -40,7 +40,7 @@ describe Ajax::QuestionController, :ajax_controller, type: :controller do
it "enqueues a QuestionWorker job" do
allow(QuestionWorker).to receive(:perform_async)
subject
expect(QuestionWorker).to have_received(:perform_async).with(expected_rcpt, user.id, Question.last.id)
expect(QuestionWorker).to have_received(:perform_async).with(user.id, Question.last.id)
end
include_examples "returns the expected response"
@ -146,55 +146,6 @@ describe Ajax::QuestionController, :ajax_controller, type: :controller do
end
end
context "when rcpt is a group" do
let(:rcpt) { "grp:foobar" }
context "when group exists" do
let(:group) { FactoryBot.create(:group, display_name: "FooBar", user: user) }
before { group }
context "when anonymousQuestion is true" do
let(:anonymous_question) { "true" }
let(:expected_question_anonymous) { false }
include_examples "creates the question", false
include_examples "enqueues a QuestionWorker job", "grp:foobar"
end
context "when anonymousQuestion is false" do
let(:anonymous_question) { "false" }
let(:expected_question_anonymous) { false }
include_examples "creates the question", false
include_examples "enqueues a QuestionWorker job", "grp:foobar"
end
end
context "when group does not exist" do
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
context "when anonymousQuestion is true" do
let(:anonymous_question) { "true" }
include_examples "does not create the question", false
include_examples "does not enqueue a QuestionWorker job"
end
context "when anonymousQuestion is false" do
let(:anonymous_question) { "false" }
include_examples "does not create the question", false
include_examples "does not enqueue a QuestionWorker job"
end
end
end
context "when rcpt is a non-existent user" do
let(:rcpt) { "tripmeister_eder" }
let(:anonymous_question) { "false" }
@ -274,12 +225,6 @@ describe Ajax::QuestionController, :ajax_controller, type: :controller do
include_examples "does not enqueue a QuestionWorker job"
end
context "when rcpt is a group" do
let(:rcpt) { "grp:foobar" }
include_examples "does not enqueue a QuestionWorker job"
end
context "when rcpt is a non-existent user" do
let(:rcpt) { "tripmeister_eder" }
let(:expected_response) do

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
FactoryBot.define do
factory :group do
factory :list do
sequence(:display_name) { |i| "#{Faker::Internet.username(specifier: 0..12, separators: %w[_])}#{i}" }
user { FactoryBot.build(:user) }
@ -9,9 +9,9 @@ FactoryBot.define do
members { [] }
end
after(:create) do |group, evaluator|
after(:create) do |list, evaluator|
evaluator.members.each do |member|
GroupMember.create(group_id: group.id, user_id: member.id)
ListMember.create(list_id: list.id, user_id: member.id)
end
end
end

View File

@ -2,16 +2,16 @@
require 'rails_helper'
RSpec.describe(Group, type: :model) do
RSpec.describe(List, type: :model) do
let(:user) { FactoryBot.build(:user) }
describe 'name mangling' do
subject do
Group.new(user: user, display_name: display_name).tap(&:validate)
List.new(user: user, display_name: display_name).tap(&:validate)
end
{
'great group' => 'great-group',
'great list' => 'great-list',
'followers' => '-followers-',
' followers ' => '-followers-',
" the game \t\nyes" => 'the-game-yes',
@ -30,11 +30,11 @@ RSpec.describe(Group, type: :model) do
describe 'validations' do
subject do
Group.new(user: user, display_name: display_name).validate
List.new(user: user, display_name: display_name).validate
end
context "when display name is 'great group' (valid)" do
let(:display_name) { 'great group' }
context "when display name is 'great list' (valid)" do
let(:display_name) { 'great list' }
it { is_expected.to be true }
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
require "rails_helper"
describe QuestionWorker do
describe "#perform" do
let(:user) { FactoryBot.create(:user) }
let(:user_id) { user.id }
let(:question) { FactoryBot.create(:question, user: user) }
let(:question_id) { question.id }
before do
5.times do
other_user = FactoryBot.create(:user)
other_user.follow(user)
end
end
subject { described_class.new.perform(user_id, question_id) }
it "places the question in the inbox of the user's followers" do
expect { subject }
.to(
change { Inbox.where(user_id: user.followers.ids, question_id: question_id, new: true).count }
.from(0)
.to(5)
)
end
end
end