thredded 0.15.5 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -7
- data/app/assets/stylesheets/thredded/_thredded.scss +1 -0
- data/app/assets/stylesheets/thredded/components/_messageboard.scss +16 -3
- data/app/assets/stylesheets/thredded/utilities/_flex.scss +3 -0
- data/app/commands/thredded/mark_all_read.rb +3 -7
- data/app/controllers/thredded/messageboards_controller.rb +4 -1
- data/app/controllers/thredded/moderation_controller.rb +7 -2
- data/app/controllers/thredded/private_topics_controller.rb +1 -5
- data/app/controllers/thredded/theme_previews_controller.rb +5 -1
- data/app/controllers/thredded/topics_controller.rb +1 -1
- data/app/forms/thredded/post_form.rb +1 -1
- data/app/forms/thredded/private_post_form.rb +1 -1
- data/app/forms/thredded/private_topic_form.rb +2 -0
- data/app/forms/thredded/topic_form.rb +2 -1
- data/app/forms/thredded/user_preferences_form.rb +5 -1
- data/app/models/concerns/thredded/post_common.rb +10 -6
- data/app/models/concerns/thredded/topic_common.rb +4 -8
- data/app/models/concerns/thredded/user_topic_read_state_common.rb +56 -62
- data/app/models/thredded/messageboard.rb +27 -1
- data/app/models/thredded/post.rb +6 -0
- data/app/models/thredded/private_topic.rb +12 -2
- data/app/models/thredded/topic.rb +8 -3
- data/app/models/thredded/user_extender.rb +0 -7
- data/app/models/thredded/user_private_topic_read_state.rb +35 -0
- data/app/models/thredded/user_topic_read_state.rb +41 -0
- data/app/view_models/thredded/messageboard_group_view.rb +24 -7
- data/app/view_models/thredded/messageboard_view.rb +50 -0
- data/app/views/thredded/messageboards/_messageboard.html.erb +8 -18
- data/app/views/thredded/messageboards/index.html.erb +6 -1
- data/app/views/thredded/messageboards/messageboard/_description.html.erb +1 -0
- data/app/views/thredded/messageboards/messageboard/_header.html.erb +6 -0
- data/app/views/thredded/messageboards/messageboard/_last_updated.html.erb +7 -0
- data/app/views/thredded/messageboards/messageboard/_meta.html.erb +19 -0
- data/app/views/thredded/messageboards/messageboard/_unread_followed_topics_badge.html.erb +6 -0
- data/app/views/thredded/preferences/_messageboards_nav_item.html.erb +1 -1
- data/app/views/thredded/private_posts/_private_post.html.erb +1 -1
- data/app/views/thredded/theme_previews/show.html.erb +10 -4
- data/app/views/thredded/topics/_topic.html.erb +1 -1
- data/app/views/thredded/topics/unread.html.erb +1 -1
- data/config/locales/de.yml +1 -0
- data/config/locales/en.yml +2 -1
- data/config/locales/es.yml +1 -0
- data/config/locales/fr.yml +1 -0
- data/config/locales/it.yml +1 -0
- data/config/locales/pl.yml +1 -0
- data/config/locales/pt-BR.yml +1 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/zh-CN.yml +1 -0
- data/db/migrate/20160329231848_create_thredded.rb +5 -0
- data/db/upgrade_migrations/20180930063614_upgrade_thredded_v0_15_to_v0_16.rb +40 -0
- data/lib/generators/thredded/install/templates/initializer.rb +4 -4
- data/lib/thredded/arel_compat.rb +18 -24
- data/lib/thredded/content_formatter.rb +1 -1
- data/lib/thredded/database_seeder.rb +7 -6
- data/lib/thredded/version.rb +1 -1
- metadata +24 -18
- data/app/views/thredded/messageboards/_messageboard_meta.html.erb +0 -13
- data/lib/tasks/thredded_tasks.rake +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a642798592ef42f04c27440bdeb2b9930273fca1bbb2cb41da3138dd10af429c
|
4
|
+
data.tar.gz: 78e334145b31fc55950554b3909864fbe6617a262fbfa07006eec2262ab526bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b134c56f3c79b38ed2b13b42e45c6c5fa7dbb54258ab941980fa7791077a8f1fdc1e76ba24d0c5658478ce762beddaf080133fe16fbd8684c467f2d1efbbe80
|
7
|
+
data.tar.gz: 560bfe2c2922595f5a93728bffb7becb25a32a39fa4ddc209ff8f5778c28ccc87cd46b0a08d106383cf2bb440c83f1a7a532bd0247683ff2e63d30028ca99e3f
|
data/README.md
CHANGED
@@ -95,7 +95,7 @@ Then, see the rest of this Readme for more information about using and customizi
|
|
95
95
|
Add the gem to your Gemfile:
|
96
96
|
|
97
97
|
```ruby
|
98
|
-
gem 'thredded', '~> 0.
|
98
|
+
gem 'thredded', '~> 0.16.0'
|
99
99
|
```
|
100
100
|
|
101
101
|
Add the Thredded [initializer] to your parent app by running the install generator.
|
@@ -104,12 +104,6 @@ Add the Thredded [initializer] to your parent app by running the install generat
|
|
104
104
|
rails generate thredded:install
|
105
105
|
```
|
106
106
|
|
107
|
-
Copy emoji images to your `public/emoji` directory.
|
108
|
-
|
109
|
-
```console
|
110
|
-
rake thredded:install:emoji
|
111
|
-
```
|
112
|
-
|
113
107
|
Thredded needs to know the base application User model name and certain columns on it. Configure
|
114
108
|
these in the initializer installed with the command above.
|
115
109
|
|
@@ -33,7 +33,6 @@
|
|
33
33
|
float: left;
|
34
34
|
font-size: 1.125rem; // 18px
|
35
35
|
line-height: 1.2;
|
36
|
-
margin-right: $thredded-small-spacing;
|
37
36
|
vertical-align: baseline;
|
38
37
|
}
|
39
38
|
|
@@ -66,6 +65,19 @@
|
|
66
65
|
vertical-align: baseline;
|
67
66
|
}
|
68
67
|
|
68
|
+
.thredded--messageboard--unread-followed-topics-count {
|
69
|
+
@extend %thredded--nav-tabs--item--badge;
|
70
|
+
align-self: baseline;
|
71
|
+
line-height: inherit;
|
72
|
+
display: flex;
|
73
|
+
}
|
74
|
+
|
75
|
+
.thredded--messageboard--unread-followed-icon {
|
76
|
+
fill: currentColor;
|
77
|
+
width: 1rem;
|
78
|
+
height: 1rem;
|
79
|
+
}
|
80
|
+
|
69
81
|
.thredded--messageboard--description {
|
70
82
|
@extend %thredded--paragraph;
|
71
83
|
clear: both;
|
@@ -96,7 +108,9 @@
|
|
96
108
|
&--header {
|
97
109
|
display: flex;
|
98
110
|
flex-wrap: wrap;
|
99
|
-
|
111
|
+
> .thredded--flex-spacer {
|
112
|
+
margin-right: $thredded-small-spacing;
|
113
|
+
}
|
100
114
|
}
|
101
115
|
&--meta {
|
102
116
|
text-align: right;
|
@@ -149,7 +163,6 @@
|
|
149
163
|
margin-bottom: $margin-y;
|
150
164
|
padding: $thredded-messageboards-grid-item-padding-y $thredded-messageboards-grid-item-padding-x;
|
151
165
|
}
|
152
|
-
|
153
166
|
}
|
154
167
|
}
|
155
168
|
}
|
@@ -1,15 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Thredded
|
4
|
+
# Marks all private topics as read for the given user.
|
4
5
|
class MarkAllRead
|
5
6
|
def self.run(user)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
unread_topics.each do |topic|
|
10
|
-
last_post = topic.posts.order_oldest_first.last
|
11
|
-
|
12
|
-
Thredded::UserPrivateTopicReadState.touch!(user.id, topic.id, last_post)
|
7
|
+
Thredded::PrivateTopic.unread(user).each do |topic|
|
8
|
+
Thredded::UserPrivateTopicReadState.touch!(user.id, topic.last_post)
|
13
9
|
end
|
14
10
|
end
|
15
11
|
end
|
@@ -8,7 +8,10 @@ module Thredded
|
|
8
8
|
after_action :verify_policy_scoped, except: %i[new create edit update]
|
9
9
|
|
10
10
|
def index
|
11
|
-
@groups = Thredded::MessageboardGroupView.grouped(
|
11
|
+
@groups = Thredded::MessageboardGroupView.grouped(
|
12
|
+
policy_scope(Thredded::Messageboard.all),
|
13
|
+
user: thredded_current_user
|
14
|
+
)
|
12
15
|
end
|
13
16
|
|
14
17
|
def new
|
@@ -54,8 +54,13 @@ module Thredded
|
|
54
54
|
|
55
55
|
def users
|
56
56
|
@users = Thredded.user_class
|
57
|
-
.
|
58
|
-
.merge(
|
57
|
+
.eager_load(:thredded_user_detail)
|
58
|
+
.merge(
|
59
|
+
Thredded::UserDetail.order(
|
60
|
+
Arel.sql('COALESCE(thredded_user_details.moderation_state, 0) ASC,'\
|
61
|
+
'thredded_user_details.moderation_state_changed_at DESC')
|
62
|
+
)
|
63
|
+
)
|
59
64
|
@query = params[:q].to_s
|
60
65
|
@users = DbTextSearch::CaseInsensitive.new(@users, Thredded.user_name_column).prefix(@query) if @query.present?
|
61
66
|
@users = @users.page(current_page)
|
@@ -32,11 +32,7 @@ module Thredded
|
|
32
32
|
.page(current_page)
|
33
33
|
return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
|
34
34
|
@posts = Thredded::TopicPostsPageView.new(thredded_current_user, private_topic, page_scope)
|
35
|
-
|
36
|
-
if thredded_signed_in?
|
37
|
-
Thredded::UserPrivateTopicReadState.touch!(thredded_current_user.id, private_topic.id, page_scope.last)
|
38
|
-
end
|
39
|
-
|
35
|
+
Thredded::UserPrivateTopicReadState.touch!(thredded_current_user.id, page_scope.last) if thredded_signed_in?
|
40
36
|
@new_post = Thredded::PrivatePostForm.new(
|
41
37
|
user: thredded_current_user, topic: private_topic, post_params: new_private_post_params
|
42
38
|
)
|
@@ -10,7 +10,11 @@ module Thredded
|
|
10
10
|
else
|
11
11
|
thredded_current_user
|
12
12
|
end
|
13
|
-
@
|
13
|
+
@messageboard_views = [
|
14
|
+
Thredded::MessageboardView.new(@messageboard, unread_topics_count: 3, unread_followed_topics_count: 2),
|
15
|
+
Thredded::MessageboardView.new(Thredded::Messageboard.first(2)[-1], unread_topics_count: 2),
|
16
|
+
Thredded::MessageboardView.new(Thredded::Messageboard.first(3)[-1]),
|
17
|
+
]
|
14
18
|
@topics = Thredded::TopicsPageView.new(@user, @messageboard.topics.page(1).limit(3))
|
15
19
|
@private_topics = Thredded::PrivateTopicsPageView.new(@user, @user.thredded_private_topics.page(1).limit(3))
|
16
20
|
topic = Thredded::Topic.new(messageboard: @messageboard, title: 'Hello', slug: 'hello', user: @user)
|
@@ -60,7 +60,7 @@ module Thredded
|
|
60
60
|
.page(current_page)
|
61
61
|
return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
|
62
62
|
@posts = Thredded::TopicPostsPageView.new(thredded_current_user, topic, page_scope)
|
63
|
-
Thredded::UserTopicReadState.touch!(thredded_current_user.id,
|
63
|
+
Thredded::UserTopicReadState.touch!(thredded_current_user.id, page_scope.last) if thredded_signed_in?
|
64
64
|
@new_post = Thredded::PostForm.new(user: thredded_current_user, topic: topic, post_params: new_post_params)
|
65
65
|
end
|
66
66
|
|
@@ -49,7 +49,7 @@ module Thredded
|
|
49
49
|
return false unless @post.valid?
|
50
50
|
was_persisted = @post.persisted?
|
51
51
|
@post.save!
|
52
|
-
Thredded::UserTopicReadState.touch!(@post.user.id, @
|
52
|
+
Thredded::UserTopicReadState.touch!(@post.user.id, @post) unless was_persisted
|
53
53
|
true
|
54
54
|
end
|
55
55
|
end
|
@@ -45,7 +45,7 @@ module Thredded
|
|
45
45
|
return false unless @post.valid?
|
46
46
|
was_persisted = @post.persisted?
|
47
47
|
@post.save!
|
48
|
-
Thredded::UserPrivateTopicReadState.touch!(@post.user.id, @
|
48
|
+
Thredded::UserPrivateTopicReadState.touch!(@post.user.id, @post) unless was_persisted
|
49
49
|
true
|
50
50
|
end
|
51
51
|
end
|
@@ -44,8 +44,10 @@ module Thredded
|
|
44
44
|
return false unless valid?
|
45
45
|
|
46
46
|
ActiveRecord::Base.transaction do
|
47
|
+
new_topic = !private_topic.persisted?
|
47
48
|
private_topic.save!
|
48
49
|
post.save!
|
50
|
+
Thredded::UserPrivateTopicReadState.read_on_first_post!(user, post) if new_topic
|
49
51
|
end
|
50
52
|
true
|
51
53
|
end
|
@@ -31,9 +31,10 @@ module Thredded
|
|
31
31
|
return false unless valid?
|
32
32
|
|
33
33
|
ActiveRecord::Base.transaction do
|
34
|
+
new_topic = !topic.persisted?
|
34
35
|
topic.save!
|
35
36
|
post.save!
|
36
|
-
Thredded::UserTopicReadState.read_on_first_post!(user,
|
37
|
+
Thredded::UserTopicReadState.read_on_first_post!(user, post) if new_topic
|
37
38
|
end
|
38
39
|
true
|
39
40
|
end
|
@@ -50,7 +50,11 @@ module Thredded
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def messageboard_groups
|
53
|
-
@messageboard_groups ||= Thredded::MessageboardGroupView.grouped(
|
53
|
+
@messageboard_groups ||= Thredded::MessageboardGroupView.grouped(
|
54
|
+
@messageboards,
|
55
|
+
user: @user,
|
56
|
+
with_unread_topics_counts: false
|
57
|
+
)
|
54
58
|
end
|
55
59
|
|
56
60
|
def notifications_for_private_topics
|
@@ -19,6 +19,8 @@ module Thredded
|
|
19
19
|
scope :order_newest_first, -> { order(created_at: :desc, id: :desc) }
|
20
20
|
|
21
21
|
before_validation :ensure_user_detail, on: :create
|
22
|
+
|
23
|
+
after_commit :update_unread_posts_count, on: %i[create destroy]
|
22
24
|
end
|
23
25
|
|
24
26
|
def avatar_url
|
@@ -48,17 +50,13 @@ module Thredded
|
|
48
50
|
end
|
49
51
|
|
50
52
|
# Marks all the posts from the given one as unread for the given user
|
51
|
-
# @param
|
52
|
-
# @param page [Integer]
|
53
|
+
# @param [Thredded.user_class] user
|
53
54
|
def mark_as_unread(user)
|
54
55
|
if previous_post.nil?
|
55
56
|
read_state = postable.user_read_states.find_by(user_id: user.id)
|
56
57
|
read_state.destroy if read_state
|
57
58
|
else
|
58
|
-
|
59
|
-
read_at: previous_post.created_at
|
60
|
-
).find_or_create_by(user_id: user.id)
|
61
|
-
read_state.update_columns(read_at: previous_post.created_at)
|
59
|
+
postable.user_read_states.touch!(user.id, previous_post, overwrite_newer: true)
|
62
60
|
end
|
63
61
|
end
|
64
62
|
|
@@ -66,6 +64,12 @@ module Thredded
|
|
66
64
|
@previous_post ||= postable.posts.order_newest_first.find_by('created_at < ?', created_at)
|
67
65
|
end
|
68
66
|
|
67
|
+
protected
|
68
|
+
|
69
|
+
def update_unread_posts_count
|
70
|
+
postable.user_read_states.update_post_counts!
|
71
|
+
end
|
72
|
+
|
69
73
|
private
|
70
74
|
|
71
75
|
def ensure_user_detail
|
@@ -50,16 +50,12 @@ module Thredded
|
|
50
50
|
# @param user [Thredded.user_class]
|
51
51
|
# @return [ActiveRecord::Relation]
|
52
52
|
def unread(user)
|
53
|
-
topics
|
53
|
+
topics = arel_table
|
54
54
|
reads_class = reflect_on_association(:user_read_states).klass
|
55
|
-
reads
|
55
|
+
reads = reads_class.arel_table
|
56
56
|
joins(topics.join(reads, Arel::Nodes::OuterJoin)
|
57
57
|
.on(topics[:id].eq(reads[:postable_id]).and(reads[:user_id].eq(user.id))).join_sources)
|
58
|
-
.merge(reads_class.where(reads[:id].eq(nil).or(reads[:
|
59
|
-
end
|
60
|
-
|
61
|
-
def post_class
|
62
|
-
reflect_on_association(:posts).klass
|
58
|
+
.merge(reads_class.where(reads[:id].eq(nil).or(reads[:unread_posts_count].not_eq(0))))
|
63
59
|
end
|
64
60
|
|
65
61
|
private
|
@@ -69,7 +65,7 @@ module Thredded
|
|
69
65
|
def read_states_by_postable_hash(user)
|
70
66
|
read_states = reflect_on_association(:user_read_states).klass
|
71
67
|
.where(user_id: user.id, postable_id: current_scope.map(&:id))
|
72
|
-
.with_page_info
|
68
|
+
.with_page_info
|
73
69
|
Thredded::TopicCommon::CachingHash.from_relation(read_states)
|
74
70
|
end
|
75
71
|
|
@@ -21,79 +21,73 @@ module Thredded
|
|
21
21
|
post.created_at <= read_at
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# Upsert gem: https://github.com/seamusabshere/upsert
|
32
|
-
state = find_or_initialize_by(user_id: user_id, postable_id: topic_id)
|
33
|
-
return unless !state.read_at? || state.read_at < post.created_at
|
34
|
-
state.update!(read_at: post.created_at)
|
35
|
-
end
|
24
|
+
def calculate_post_counts
|
25
|
+
unread_posts_count, read_posts_count =
|
26
|
+
self.class.visible_posts_scope(user)
|
27
|
+
.where(postable_id: postable_id)
|
28
|
+
.pluck(*self.class.post_counts_arel(read_at))[0]
|
29
|
+
{ unread_posts_count: unread_posts_count || 0, read_posts_count: read_posts_count || 0 }
|
30
|
+
end
|
36
31
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
32
|
+
module ClassMethods
|
33
|
+
delegate :post_class, to: :topic_class
|
40
34
|
|
41
35
|
# Adds `first_unread_post_page` and `last_read_post_page` columns onto the scope.
|
42
36
|
# Skips the records that have no read posts.
|
43
|
-
def with_page_info(
|
44
|
-
posts_per_page: post_class.default_per_page, posts_scope: post_class.all
|
45
|
-
)
|
37
|
+
def with_page_info(posts_per_page: post_class.default_per_page)
|
46
38
|
states = arel_table
|
47
|
-
|
48
|
-
if
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
states_select_manager = Thredded::ArelCompat.new_arel_select_manager(
|
53
|
-
Arel::Nodes::TableAlias.new(Thredded::ArelCompat.relation_to_arel(self_relation), table_name)
|
54
|
-
)
|
55
|
-
end
|
56
|
-
read = if posts_scope == post_class.unscoped
|
57
|
-
post_class.arel_table
|
58
|
-
else
|
59
|
-
posts_subquery = Thredded::ArelCompat.relation_to_arel(posts_scope)
|
60
|
-
Arel::Nodes::TableAlias.new(posts_subquery, 'read_posts')
|
61
|
-
end
|
62
|
-
unread_topics = topic_class.arel_table
|
63
|
-
page_info =
|
64
|
-
states_select_manager
|
65
|
-
.project(
|
66
|
-
states[:id],
|
67
|
-
Arel::Nodes::Case.new(unread_topics[:id].not_eq(nil))
|
68
|
-
.when(Thredded::ArelCompat.true_value(self)).then(
|
69
|
-
Arel::Nodes::Addition.new(
|
70
|
-
Thredded::ArelCompat.integer_division(self, read[:id].count, posts_per_page), 1
|
71
|
-
)
|
72
|
-
).else(nil)
|
73
|
-
.as('first_unread_post_page'),
|
39
|
+
selects = []
|
40
|
+
selects << states[Arel.star] if !is_a?(ActiveRecord::Relation) || select_values.empty?
|
41
|
+
selects += [
|
42
|
+
Arel::Nodes::Case.new(states[:unread_posts_count].not_eq(0))
|
43
|
+
.when(Thredded::ArelCompat.true_value(self)).then(
|
74
44
|
Arel::Nodes::Addition.new(
|
75
|
-
Thredded::ArelCompat.integer_division(self,
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
)
|
80
|
-
.
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
45
|
+
Thredded::ArelCompat.integer_division(self, states[:read_posts_count], posts_per_page), 1
|
46
|
+
)
|
47
|
+
).else(nil).as('first_unread_post_page'),
|
48
|
+
Arel::Nodes::Addition.new(
|
49
|
+
Thredded::ArelCompat.integer_division(self, states[:read_posts_count], posts_per_page),
|
50
|
+
Arel::Nodes::Case.new(Arel::Nodes::InfixOperation.new(:%, states[:read_posts_count], posts_per_page))
|
51
|
+
.when(0).then(0).else(1)
|
52
|
+
).as('last_read_post_page')
|
53
|
+
]
|
54
|
+
select(selects)
|
55
|
+
end
|
86
56
|
|
87
|
-
|
88
|
-
|
89
|
-
|
57
|
+
# Calculates and saves the `unread_posts_count` and `read_posts_count` columns.
|
58
|
+
def update_post_counts!
|
59
|
+
id_counts = calculate_post_counts_for_users(Thredded.user_class.where(id: distinct.select(:user_id)))
|
60
|
+
transaction do
|
61
|
+
id_counts.each do |(id, unread_posts_count, read_posts_count)|
|
62
|
+
where(id: id).update_all(unread_posts_count: unread_posts_count, read_posts_count: read_posts_count)
|
63
|
+
end
|
64
|
+
end
|
90
65
|
end
|
91
66
|
|
92
|
-
|
93
|
-
|
67
|
+
# @param [DateTime, Arel::Node] read_at
|
68
|
+
# @param [Arel::Table] posts
|
69
|
+
# @return [[Arel::Node, Arel::Node]] `unread_posts_count` and `read_posts_count` nodes.
|
70
|
+
def post_counts_arel(read_at, posts: post_class.arel_table)
|
71
|
+
[
|
72
|
+
Arel::Nodes::Sum.new(
|
73
|
+
[Arel::Nodes::Case.new(posts[:created_at].gt(read_at))
|
74
|
+
.when(Thredded::ArelCompat.true_value(self)).then(1).else(0)]
|
75
|
+
).as('unread_posts_count'),
|
76
|
+
Arel::Nodes::Sum.new(
|
77
|
+
[Arel::Nodes::Case.new(posts[:created_at].gt(read_at))
|
78
|
+
.when(Thredded::ArelCompat.true_value(self)).then(0).else(1)]
|
79
|
+
).as('read_posts_count')
|
80
|
+
]
|
94
81
|
end
|
95
82
|
|
96
|
-
|
83
|
+
# @return [Array<[id, unread_posts_count, read_posts_count]>]
|
84
|
+
def calculate_post_counts
|
85
|
+
states = arel_table
|
86
|
+
posts = post_class.arel_table
|
87
|
+
joins(states.join(posts).on(states[:postable_id].eq(posts[:postable_id])).join_sources)
|
88
|
+
.group(states[:id])
|
89
|
+
.pluck(states[:id], *post_counts_arel(states[:read_at], posts: posts))
|
90
|
+
end
|
97
91
|
end
|
98
92
|
end
|
99
93
|
end
|
@@ -4,7 +4,7 @@ module Thredded
|
|
4
4
|
class Messageboard < ActiveRecord::Base
|
5
5
|
extend FriendlyId
|
6
6
|
friendly_id :slug_candidates,
|
7
|
-
use:
|
7
|
+
use: %i[slugged reserved],
|
8
8
|
# Avoid route conflicts
|
9
9
|
reserved_words: ::Thredded::FriendlyIdReservedWordsAndPagination.new(
|
10
10
|
%w[
|
@@ -51,6 +51,11 @@ module Thredded
|
|
51
51
|
through: :recently_active_user_details,
|
52
52
|
source: :user
|
53
53
|
|
54
|
+
has_many :user_topic_read_states,
|
55
|
+
class_name: 'Thredded::UserTopicReadState',
|
56
|
+
inverse_of: :messageboard,
|
57
|
+
dependent: :delete_all
|
58
|
+
|
54
59
|
belongs_to :group,
|
55
60
|
inverse_of: :messageboards,
|
56
61
|
foreign_key: :messageboard_group_id,
|
@@ -114,5 +119,26 @@ module Thredded
|
|
114
119
|
[:name, '-board']
|
115
120
|
]
|
116
121
|
end
|
122
|
+
|
123
|
+
class << self
|
124
|
+
# @param [Thredded.user_class] user
|
125
|
+
# @param [ActiveRecord::Relation<Thredded::Topic>] topics_scope
|
126
|
+
def unread_topics_counts(user:, topics_scope: Thredded::Topic.all)
|
127
|
+
messageboards = arel_table
|
128
|
+
read_states = Thredded::UserTopicReadState.arel_table
|
129
|
+
topics = topics_scope.arel_table
|
130
|
+
joins(:topics).merge(topics_scope).joins(
|
131
|
+
messageboards.outer_join(read_states).on(
|
132
|
+
messageboards[:id].eq(read_states[:messageboard_id])
|
133
|
+
.and(read_states[:postable_id].eq(topics[:id]))
|
134
|
+
.and(read_states[:user_id].eq(user.id))
|
135
|
+
.and(read_states[:unread_posts_count].eq(0))
|
136
|
+
).join_sources
|
137
|
+
).group(messageboards[:id]).pluck(
|
138
|
+
:id,
|
139
|
+
Arel::Nodes::Subtraction.new(topics[:id].count, read_states[:id].count)
|
140
|
+
).to_h
|
141
|
+
end
|
142
|
+
end
|
117
143
|
end
|
118
144
|
end
|