Rename Groups to Lists

haha regexp go brrr

special thanks to @seatsea for helping me out with the French locales
This commit is contained in:
Georg Gadinger 2020-05-25 18:04:54 +02:00
parent 0115d3c795
commit ea0685136e
47 changed files with 390 additions and 376 deletions

View File

@ -19,7 +19,7 @@
# local requires to be seen by everyone: # local requires to be seen by everyone:
#= require_tree ./answerbox #= require_tree ./answerbox
#= require_tree ./questionbox #= require_tree ./questionbox
#= require groups #= require lists
#= require inbox #= require inbox
#= require memes #= require memes
#= require notifications #= 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 = $(this)
box.attr 'disabled', 'disabled' 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 boxChecked = box[0].checked
count += if boxChecked then 1 else -1 count += if boxChecked then 1 else -1
$.ajax $.ajax
url: '/ajax/group_membership' url: '/ajax/list_membership'
type: 'POST' type: 'POST'
data: data:
user: box[0].dataset.user user: box[0].dataset.user
group: groupName list: listName
add: boxChecked add: boxChecked
success: (data, status, jqxhr) -> success: (data, status, jqxhr) ->
if data.success if data.success
box[0].checked = if data.checked? then data.checked else !boxChecked 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 showNotification data.message, data.success
error: (jqxhr, status, error) -> error: (jqxhr, status, error) ->
box[0].checked = false box[0].checked = false
@ -29,19 +29,19 @@
box.removeAttr "disabled" 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 if evt.which == 13 # return key
evt.preventDefault() 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 = $(this)
btn.button "loading" btn.button "loading"
input = ($ "input#new-group-name") input = ($ "input#new-list-name")
$.ajax $.ajax
url: '/ajax/create_group' url: '/ajax/create_list'
type: 'POST' type: 'POST'
data: data:
name: input.val() name: input.val()
@ -49,7 +49,7 @@ $(document).on "keyup", "input#new-group-name", (evt) ->
dataType: 'json' dataType: 'json'
success: (data, status, jqxhr) -> success: (data, status, jqxhr) ->
if data.success if data.success
($ "ul.list-group.groups--list").append(data.render) ($ "#lists-list ul.list-group").append(data.render)
input.val '' input.val ''
showNotification data.message, data.success showNotification data.message, data.success
error: (jqxhr, status, error) -> error: (jqxhr, status, error) ->
@ -59,14 +59,14 @@ $(document).on "keyup", "input#new-group-name", (evt) ->
btn.button "reset" btn.button "reset"
($ document).on "click", "a#delete-group", (ev) -> ($ document).on "click", "a#delete-list", (ev) ->
ev.preventDefault() ev.preventDefault()
btn = $(this) btn = $(this)
group = btn[0].dataset.group list = btn[0].dataset.list
swal swal
title: translate('frontend.group.title') title: translate('frontend.list.title')
text: translate('frontend.group.text') text: translate('frontend.list.text')
type: "warning" type: "warning"
showCancelButton: true showCancelButton: true
confirmButtonColor: "#DD6B55" confirmButtonColor: "#DD6B55"
@ -75,14 +75,14 @@ $(document).on "keyup", "input#new-group-name", (evt) ->
closeOnConfirm: true closeOnConfirm: true
, -> , ->
$.ajax $.ajax
url: '/ajax/destroy_group' url: '/ajax/destroy_list'
type: 'POST' type: 'POST'
data: data:
group: group list: list
dataType: 'json' dataType: 'json'
success: (data, status, jqxhr) -> success: (data, status, jqxhr) ->
if data.success if data.success
($ "li.list-group-item#group-#{group}").slideUp() ($ "li.list-group-item#list-#{list}").slideUp()
showNotification data.message, data.success showNotification data.message, data.success
error: (jqxhr, status, error) -> error: (jqxhr, status, error) ->
console.log jqxhr, status, error console.log jqxhr, status, error

View File

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

View File

@ -56,7 +56,7 @@ class Ajax::QuestionController < AjaxController
elsif params[:rcpt].start_with? 'grp:' elsif params[:rcpt].start_with? 'grp:'
unless current_user.nil? unless current_user.nil?
begin begin
current_user.groups.find_by_name!(params[:rcpt].sub 'grp:', '') current_user.lists.find_by_name!(params[:rcpt].sub 'grp:', '')
QuestionWorker.perform_async params[:rcpt], current_user.id, question.id QuestionWorker.perform_async params[:rcpt], current_user.id, question.id
rescue ActiveRecord::RecordNotFound => e rescue ActiveRecord::RecordNotFound => e
NewRelic::Agent.notice_error(e) NewRelic::Agent.notice_error(e)

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 end
# endregion # endregion
# region Groups # region Lists
def groups def lists
@user = User.where('LOWER(screen_name) = ?', params[:username].downcase).first! @user = User.where('LOWER(screen_name) = ?', params[:username].downcase).first!
@groups = if current_user == @user @lists = if current_user == @user
@user.groups @user.lists
else else
@user.groups.where(private: false) @user.lists.where(private: false)
end.all end.all
end end
# endregion # endregion

View File

@ -166,7 +166,7 @@ module ApplicationHelper
user_title user, "comments" user_title user, "comments"
end end
def group_title(group) def list_title(list)
generate_title group.name generate_title list.name
end end
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 # frozen_string_literal: true
class Group < ApplicationRecord class List < ApplicationRecord
include Group::TimelineMethods include List::TimelineMethods
belongs_to :user belongs_to :user
has_many :group_members, dependent: :destroy has_many :list_members, dependent: :destroy
validates :name, length: { minimum: 1 } validates :name, length: { minimum: 1 }
validates :display_name, length: { maximum: 30 } validates :display_name, length: { maximum: 30 }
@ -15,13 +15,13 @@ class Group < ApplicationRecord
self.name = '-followers-' if self.name == 'followers' self.name = '-followers-' if self.name == 'followers'
end end
alias members group_members alias members list_members
def add_member(user) def add_member(user)
GroupMember.create! group: self, user: user ListMember.create! list: self, user: user
end end
def remove_member(user) def remove_member(user)
GroupMember.where(group: self, user: user).first!.destroy ListMember.where(list: self, user: user).first!.destroy
end end
end end

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
module Group::TimelineMethods module List::TimelineMethods
include CursorPaginatable include CursorPaginatable
define_cursor_paginator :cursored_timeline, :timeline define_cursor_paginator :cursored_timeline, :timeline
# @return [Array] the groups' timeline # @return [Array] the lists' timeline
def timeline def timeline
Answer.where('user_id in (?)', members.pluck(:user_id)).order(:created_at).reverse_order Answer.where('user_id in (?)', members.pluck(:user_id)).order(:created_at).reverse_order
end 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 :reports, dependent: :destroy
has_many :moderation_comments, dependent: :destroy has_many :moderation_comments, dependent: :destroy
has_many :moderation_votes, dependent: :destroy has_many :moderation_votes, dependent: :destroy
has_many :groups, dependent: :destroy has_many :lists, dependent: :destroy
has_many :group_memberships, class_name: "GroupMember", foreign_key: 'user_id', dependent: :destroy has_many :list_memberships, class_name: "ListMember", foreign_key: 'user_id', dependent: :destroy
has_many :subscriptions, dependent: :destroy has_many :subscriptions, dependent: :destroy
@ -110,10 +110,10 @@ class User < ApplicationRecord
friends.include? target_user friends.include? target_user
end end
# @param group [Group] # @param list [List]
# @return [Boolean] true if +self+ is a member of +group+ # @return [Boolean] true if +self+ is a member of +list+
def member_of?(group) def member_of?(list)
group_memberships.pluck(:group_id).include? group.id list_memberships.pluck(:list_id).include? list.id
end end
# answers a question # answers a question

View File

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

View File

@ -10,7 +10,7 @@
= render 'tabs/profile', user: @user = render 'tabs/profile', user: @user
= yield = yield
- if user_signed_in? - if user_signed_in?
= render 'modal/group', user: @user = render 'modal/list', user: @user
- if current_user.mod? && @user != current_user - if current_user.mod? && @user != current_user
= render 'modal/privileges', user: @user = render 'modal/privileges', user: @user
= render 'modal/ban', 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 } } %button.btn.btn-default#load-more-btn{ type: :button, data: { last_id: @timeline_last_id } }
= t 'views.actions.load' = t 'views.actions.load'
- provide(:title, group_title(@group)) - provide(:title, list_title(@list))
- parent_layout 'feed' - parent_layout 'feed'

View File

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

@ -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 %ul.nav.navbar-nav
- unless @user.nil? - unless @user.nil?
- unless @user == current_user - unless @user == current_user
%li.nav-item.d-none.d-sm-block{ data: { toggle: 'tooltip', placement: 'bottom' }, title: 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-group-memberships', toggle: :modal } } %a.nav-link{ href: '#', data: { target: '#modal-list-memberships', toggle: :modal } }
%i.fa.fa-users.hidden-xs %i.fa.fa-list.hidden-xs
%span.d-none.d-sm-inline.d-md-none= t('views.actions.group') %span.d-none.d-sm-inline.d-md-none= t('views.actions.list')
= render 'navigation/main/notifications' = render 'navigation/main/notifications'
%li.nav-item.d-none.d-sm-block{ data: { toggle: 'tooltip', placement: 'bottom' }, title: t('views.actions.ask_question') } %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 } } %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 = current_user.display_name
.profile__screen-name .profile__screen-name
= current_user.screen_name = current_user.screen_name
- unless @group.nil? - unless @list.nil?
.card .card
.card-header= t('views.group.members') .card-header= t('views.list.members')
.card-body .card-body
- if @group.members.empty? - if @list.members.empty?
%p.text-muted No members yet! %p.text-muted No members yet.
- @group.members.each do |member| - @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 } } %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) } %img.avatar-xs{ src: member.user.profile_picture.url(:medium) }

View File

@ -65,11 +65,10 @@
.row .row
.col-md-4.col-sm-4.col-xs-12 .col-md-4.col-sm-4.col-xs-12
%h3 %h3
%i.fa.fa-fw.fa-users.text-primary %i.fa.fa-fw.fa-list.text-primary
Groups Lists
%p %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.
.col-md-4.col-sm-4.col-xs-12 .col-md-4.col-sm-4.col-xs-12
%h3 %h3
%i.fa.fa-fw.fa-image.text-primary %i.fa.fa-fw.fa-image.text-primary

View File

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

View File

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

@ -14,7 +14,7 @@ class QuestionWorker
Inbox.create(user_id: f.id, question_id: question_id, new: true) Inbox.create(user_id: f.id, question_id: question_id, new: true)
end end
elsif rcpt.start_with? 'grp:' elsif rcpt.start_with? 'grp:'
user.groups.find_by_name!(rcpt.sub 'grp:', '').members.each do |m| user.lists.find_by_name!(rcpt.sub 'grp:', '').members.each do |m|
Inbox.create(user_id: m.user.id, question_id: question_id, new: true) Inbox.create(user_id: m.user.id, question_id: question_id, new: true)
end end
else else

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,9 +14,9 @@ fr:
fail: fail:
subscribe: "Échec de l'abonnement à la réponse." subscribe: "Échec de l'abonnement à la réponse."
unsubscribe: "Échec du désabonnement à la réponse." unsubscribe: "Échec du désabonnement à la réponse."
group: list:
confirm: confirm:
title: "Voulez-vous vraiment supprimer ce groupe ?" title: "Voulez-vous vraiment supprimer cette liste ?"
text: "Vous ne pourrez pas le récupérer." text: "Vous ne pourrez pas le récupérer."
destroy_question: destroy_question:
confirm: confirm:
@ -97,18 +97,18 @@ fr:
destroy: destroy:
fail: "Vous ne suivez pas cet utilisateur." fail: "Vous ne suivez pas cet utilisateur."
okay: "Vous ne suivez plus cet utilisateur." okay: "Vous ne suivez plus cet utilisateur."
group: list:
create: create:
noname: "Veuillez donner un nom à ce groupe." noname: "Veuillez donner un nom à cette liste."
toolong: "Le nom du groupe est trop long (30 caractères max.)" toolong: "Le nom de la liste est trop long (30 caractères max.)"
notfound: "L'utilisateur est introuvable." notfound: "L'utilisateur est introuvable."
exists: "Ce groupe existe déjà." exists: "Cette liste existe déjà."
okay: "Le groupe a été créé avec succès." okay: "La liste a été créé avec succès."
destroy: destroy:
notfound: "Le groupe est introuvable." notfound: "La liste est introuvable."
okay: "Le groupe a été supprimé avec succès." okay: "La liste a été supprimé avec succès."
membership: membership:
notfound: "Le groupe est introuvable." notfound: "La liste est introuvable."
add: "L'utilisateur a été ajouté avec succès." add: "L'utilisateur a été ajouté avec succès."
remove: "L'utilisateur a été enlevé avec succès." remove: "L'utilisateur a été enlevé avec succès."
inbox: inbox:
@ -152,7 +152,7 @@ fr:
okay: "La question a été supprimée avec succès." okay: "La question a été supprimée avec succès."
create: create:
rec_inv: "Votre question est trop longue." 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." okay: "La question a été posée avec succès."
report: report:
create: create:
@ -237,8 +237,8 @@ fr:
terms: "Conditions générales d'utilisation" terms: "Conditions générales d'utilisation"
privacy: "Politique de confidentialité" privacy: "Politique de confidentialité"
about: "À propos" about: "À propos"
group: list:
title: "Groupe" title: "Liste"
members: "Membres" members: "Membres"
actions: actions:
title: "Actions" title: "Actions"
@ -256,7 +256,7 @@ fr:
load: "Charger plus" load: "Charger plus"
follow: "Suivre" follow: "Suivre"
unfollow: "Ne plus suivre" unfollow: "Ne plus suivre"
group: "Gérer l'appartenance à un groupe" list: "Gérer l'appartenance à une liste"
ban: "Contrôle des bannissements" ban: "Contrôle des bannissements"
privilege: "Vérifier les privilèges de %{user}" privilege: "Vérifier les privilèges de %{user}"
save: "Enregistrer les modifications" save: "Enregistrer les modifications"
@ -418,7 +418,7 @@ fr:
modal: modal:
ask: ask:
title: "Poser une question à vos abonnés" title: "Poser une question à vos abonnés"
choose: "Choisir un groupe :" choose: "Choisir une liste :"
loading: "Envoi..." loading: "Envoi..."
bancontrol: bancontrol:
title: "Centre de contrôle des bannissements" title: "Centre de contrôle des bannissements"
@ -426,13 +426,13 @@ fr:
permanent: "Définitivement ?" permanent: "Définitivement ?"
reason: "Raison" reason: "Raison"
hammertime: "Que le Marteau s'abatte" hammertime: "Que le Marteau s'abatte"
group: list:
title: "Gérer l'appartenance à un groupe" title: "Gérer l'appartenance à une liste"
tabs: tabs:
main: "Groupes" main: "Listes"
create: "Créer un nouveau groupe" create: "Créer une nouvelle liste"
create: "Créer" create: "Créer"
name: "Nom du groupe" name: "Nom de la liste"
members: "membres" members: "membres"
privilege: privilege:
blogger: "L'utilisateur obtient ce titre s'il a posté un article (agréable) à propos de Retrospring." 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: fail:
subscribe: "Impossibile iscriversi alla risposta." subscribe: "Impossibile iscriversi alla risposta."
unsubscribe: "Impossibile disiscriversi dalla risposta." unsubscribe: "Impossibile disiscriversi dalla risposta."
group: list:
confirm: confirm:
title: "Vuoi davvero eliminare questo gruppo?" title: "Vuoi davvero eliminare questo gruppo?"
text: "Non sarai più in grado di recuperare questo gruppo." text: "Non sarai più in grado di recuperare questo gruppo."
@ -94,7 +94,7 @@ it:
destroy: destroy:
fail: "Non stai seguendo quell'utente." fail: "Non stai seguendo quell'utente."
okay: "Non stai più seguendo quell'utente." okay: "Non stai più seguendo quell'utente."
group: list:
create: create:
noname: "Per favore, dai un nome a quel gruppo." noname: "Per favore, dai un nome a quel gruppo."
toolong: "Nome del gruppo troppo grande (massimo 30 caratteri)" toolong: "Nome del gruppo troppo grande (massimo 30 caratteri)"
@ -236,7 +236,7 @@ it:
terms: "Termini di Servizio" terms: "Termini di Servizio"
privacy: "Normativa sulla privacy" privacy: "Normativa sulla privacy"
about: "Informazioni" about: "Informazioni"
group: list:
title: "Gruppo" title: "Gruppo"
members: "Membri" members: "Membri"
actions: actions:
@ -255,7 +255,7 @@ it:
load: "Carica altro" load: "Carica altro"
follow: "Segui" follow: "Segui"
unfollow: "Non seguire più" unfollow: "Non seguire più"
group: "Impostazioni sui gruppi" list: "Impostazioni sui gruppi"
ban: "Controlla Ban" ban: "Controlla Ban"
privilege: "Controlla i privilegi dell'%{user}" privilege: "Controlla i privilegi dell'%{user}"
save: "Salva le impostazioni" save: "Salva le impostazioni"
@ -425,7 +425,7 @@ it:
permanent: Permanentemente? permanent: Permanentemente?
reason: "Motivo" reason: "Motivo"
hammertime: "Tempo del ban" hammertime: "Tempo del ban"
group: list:
title: "Gestisci appartenenza a gruppi" title: "Gestisci appartenenza a gruppi"
tabs: tabs:
main: "Gruppi" main: "Gruppi"

View File

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

View File

@ -102,16 +102,16 @@ Rails.application.routes.draw do
match '/create_comment', to: 'comment#create', via: :post, as: :create_comment match '/create_comment', to: 'comment#create', via: :post, as: :create_comment
match '/destroy_comment', to: 'comment#destroy', via: :post, as: :destroy_comment match '/destroy_comment', to: 'comment#destroy', via: :post, as: :destroy_comment
match '/report', to: 'report#create', via: :post, as: :report match '/report', to: 'report#create', via: :post, as: :report
match '/create_group', to: 'group#create', via: :post, as: :create_group match '/create_list', to: 'list#create', via: :post, as: :create_list
match '/destroy_group', to: 'group#destroy', via: :post, as: :destroy_group match '/destroy_list', to: 'list#destroy', via: :post, as: :destroy_list
match '/group_membership', to: 'group#membership', via: :post, as: :group_membership match '/list_membership', to: 'list#membership', via: :post, as: :list_membership
match '/subscribe', to: 'subscription#subscribe', via: :post, as: :subscribe_answer match '/subscribe', to: 'subscription#subscribe', via: :post, as: :subscribe_answer
match '/unsubscribe', to: 'subscription#unsubscribe', via: :post, as: :unsubscribe_answer match '/unsubscribe', to: 'subscription#unsubscribe', via: :post, as: :unsubscribe_answer
end end
match '/discover', to: 'discover#index', via: :get, as: :discover 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 '/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'} 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/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/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/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} 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 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. # 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 # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" 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" t.index ["user_id", "created_at"], name: "index_comments_on_user_id_and_created_at"
end 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| create_table "inboxes", id: :serial, force: :cascade do |t|
t.integer "user_id" t.integer "user_id"
t.integer "question_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" t.index ["user_id"], name: "index_inboxes_on_user_id"
end 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| create_table "moderation_comments", id: :serial, force: :cascade do |t|
t.integer "report_id" t.integer "report_id"
t.integer "user_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. 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 #### 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 #### 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. 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 #### Non-personal Identification Information

View File

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

View File

@ -146,12 +146,12 @@ describe Ajax::QuestionController, :ajax_controller, type: :controller do
end end
end end
context "when rcpt is a group" do context "when rcpt is a list" do
let(:rcpt) { "grp:foobar" } let(:rcpt) { "grp:foobar" }
context "when group exists" do context "when list exists" do
let(:group) { FactoryBot.create(:group, display_name: "FooBar", user: user) } let(:list) { FactoryBot.create(:list, display_name: "FooBar", user: user) }
before { group } before { list }
context "when anonymousQuestion is true" do context "when anonymousQuestion is true" do
let(:anonymous_question) { "true" } let(:anonymous_question) { "true" }
@ -170,7 +170,7 @@ describe Ajax::QuestionController, :ajax_controller, type: :controller do
end end
end end
context "when group does not exist" do context "when list does not exist" do
let(:expected_response) do let(:expected_response) do
{ {
"success" => false, "success" => false,
@ -274,7 +274,7 @@ describe Ajax::QuestionController, :ajax_controller, type: :controller do
include_examples "does not enqueue a QuestionWorker job" include_examples "does not enqueue a QuestionWorker job"
end end
context "when rcpt is a group" do context "when rcpt is a list" do
let(:rcpt) { "grp:foobar" } let(:rcpt) { "grp:foobar" }
include_examples "does not enqueue a QuestionWorker job" include_examples "does not enqueue a QuestionWorker job"

View File

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

View File

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