Skip to content

Commit 8714649

Browse files
committed
fix conversation count bug, closes #5428
simplified the logic a bit, added specs. note that CPs are never auto- marked-as-read any more for the sender (though newly created CPs are now "read" by default, until the first message is added by someone other than the user) Change-Id: I3d113fe6135d1f8c6f07b52a245a6de7d25b7f9b Reviewed-on: https://gerrit.instructure.com/5337 Reviewed-by: Jacob Fugal <jacob@instructure.com> Tested-by: Hudson <hudson@instructure.com>
1 parent 9ad7f12 commit 8714649

5 files changed

Lines changed: 136 additions & 26 deletions

File tree

app/models/conversation.rb

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ def self.initiate(user_ids, private)
5454
user_ids.each do |user_id|
5555
participant = conversation.conversation_participants.build
5656
participant.user_id = user_id
57+
participant.workflow_state = 'read'
5758
participant.save!
5859
end
59-
User.update_all('unread_conversations_count = unread_conversations_count + 1', :id => user_ids)
6060
end
6161
conversation
6262
end
@@ -70,13 +70,15 @@ def add_participants(current_user, user_ids)
7070
user_ids -= conversation_participants.map(&:user_id)
7171
next if user_ids.empty?
7272

73-
User.update_all('unread_conversations_count = unread_conversations_count + 1', :id => user_ids)
74-
7573
last_message_at = conversation_messages.human.first.created_at
74+
raise "can't add participants if there are no messages" unless last_message_at
7675
num_messages = conversation_messages.human.size
76+
77+
User.update_all('unread_conversations_count = unread_conversations_count + 1', :id => user_ids)
7778
user_ids.each do |user_id|
7879
participant = conversation_participants.build
7980
participant.user_id = user_id
81+
participant.workflow_state = 'unread'
8082
participant.last_message_at = last_message_at
8183
participant.message_count = num_messages
8284
participant.save!
@@ -154,16 +156,10 @@ def add_message(current_user, body, options = {})
154156
["(last_message_at IS NULL OR subscribed) AND user_id <> ?", current_user.id]
155157
)
156158

157-
# for the sender, update the timestamps
158-
# marking it as read is a ui concern, unless it's the first message (e.g. we might be sending from outside the inbox)
159+
# for the sender, update the timestamps (marking it as read is always a ui concern)
159160
if (sender_conversation = conversation_participants.find_by_user_id(current_user.id)) && (options[:update_for_sender] || sender_conversation.last_message_at)
160-
mark_as_read = options[:update_for_sender] && sender_conversation.last_message_at.nil?
161-
if mark_as_read && sender_conversation.unread? && sender_conversation.last_message_at
162-
User.update_all 'unread_conversations_count = unread_conversations_count - 1', :id => current_user.id
163-
end
164161
sender_conversation.last_message_at = Time.now.utc
165162
sender_conversation.last_authored_at = Time.now.utc
166-
sender_conversation.workflow_state = 'read' if mark_as_read
167163
sender_conversation.save
168164
end
169165

app/models/conversation_participant.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,18 @@ def remove_messages(*to_delete)
142142
messages.clear if messages.all?(&:generated?)
143143
end
144144
update_cached_data
145+
save
145146
end
146147

147148
def subscribed=(value)
148149
super
149-
if subscribed?
150-
self.workflow_state = :unread
151-
update_cached_data(false)
152-
else
153-
mark_as_read
150+
if subscribed_changed?
151+
if subscribed?
152+
update_cached_data(false)
153+
self.workflow_state = 'unread' if last_message_at_changed? && last_message_at > last_message_at_was
154+
else
155+
self.workflow_state = 'read' if unread?
156+
end
154157
end
155158
subscribed?
156159
end
@@ -200,7 +203,6 @@ def update_cached_data(recalculate_count=true)
200203
self.has_media_objects = false
201204
self.label = nil
202205
end
203-
save
204206
end
205207

206208
def update_unread_count
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class ConversationsCountFix < ActiveRecord::Migration
2+
def self.up
3+
execute "UPDATE conversation_participants SET workflow_state = 'read' WHERE workflow_state = 'unread' AND last_message_at IS NULL"
4+
5+
execute <<-SQL
6+
UPDATE users
7+
SET unread_conversations_count = (
8+
SELECT COUNT(*)
9+
FROM conversation_participants
10+
WHERE workflow_state = 'unread'
11+
AND last_message_at IS NOT NULL
12+
AND user_id = users.id
13+
)
14+
SQL
15+
end
16+
17+
def self.down
18+
end
19+
end

spec/controllers/conversations_controller_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def conversation(num_other_users = 1)
160160

161161
post 'workflow_event', :conversation_id => @conversation.conversation_id, :event => "mark_as_unread"
162162
response.should be_success
163-
@conversation.unread?.should be_true
163+
@conversation.reload.unread?.should be_true
164164
end
165165
end
166166

spec/models/conversation_spec.rb

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,11 @@
9999
end
100100

101101
context "unread counts" do
102-
it "should increment when creating a conversation" do
102+
it "should increment for recipients when sending the first message in a conversation" do
103103
sender = user
104104
recipient = user
105105
root_convo = Conversation.initiate([sender.id, recipient.id], false)
106+
ConversationParticipant.unread.size.should eql 0 # only once the first message is added
106107
root_convo.add_message(sender, 'test')
107108
sender.reload.unread_conversations_count.should eql 0
108109
sender.conversations.unread.size.should eql 0
@@ -117,7 +118,7 @@
117118
unsubscribed_guy = user
118119
root_convo = Conversation.initiate([sender.id, unread_guy.id, subscribed_guy.id, unsubscribed_guy.id], false)
119120
root_convo.add_message(sender, 'test')
120-
121+
121122
unread_guy.reload.unread_conversations_count.should eql 1
122123
unread_guy.conversations.unread.size.should eql 1
123124
subscribed_guy.conversations.first.mark_as_read
@@ -142,7 +143,7 @@
142143
unread_guy = user
143144
root_convo = Conversation.initiate([sender.id, unread_guy.id], false)
144145
root_convo.add_message(sender, 'test')
145-
146+
146147
unread_guy.reload.unread_conversations_count.should eql 1
147148
unread_guy.conversations.unread.size.should eql 1
148149
unread_guy.conversations.first.remove_messages(:all)
@@ -155,7 +156,7 @@
155156
unread_guy = user
156157
root_convo = Conversation.initiate([sender.id, unread_guy.id], false)
157158
root_convo.add_message(sender, 'test')
158-
159+
159160
unread_guy.reload.unread_conversations_count.should eql 1
160161
unread_guy.conversations.unread.size.should eql 1
161162
unread_guy.conversations.first.mark_as_read
@@ -169,7 +170,7 @@
169170
root_convo = Conversation.initiate([sender.id, unread_guy.id], false)
170171
root_convo.add_message(sender, 'test')
171172
unread_guy.conversations.first.mark_as_read
172-
173+
173174
unread_guy.reload.unread_conversations_count.should eql 0
174175
unread_guy.conversations.unread.size.should eql 0
175176
unread_guy.conversations.first.mark_as_unread
@@ -178,6 +179,81 @@
178179
end
179180
end
180181

182+
context "subscription" do
183+
it "should mark-as-read when unsubscribing iff it was unread" do
184+
sender = user
185+
subscription_guy = user
186+
archive_guy = user
187+
root_convo = Conversation.initiate([sender.id, archive_guy.id, subscription_guy.id], false)
188+
root_convo.add_message(sender, 'test')
189+
190+
subscription_guy.reload.unread_conversations_count.should eql 1
191+
subscription_guy.conversations.unread.size.should eql 1
192+
193+
subscription_guy.conversations.first.update_attributes(:subscribed => false)
194+
subscription_guy.reload.unread_conversations_count.should eql 0
195+
subscription_guy.conversations.unread.size.should eql 0
196+
197+
archive_guy.conversations.first.archive!
198+
archive_guy.conversations.first.update_attributes(:subscribed => false)
199+
archive_guy.conversations.archived.size.should eql 1
200+
end
201+
202+
it "should mark-as-unread when re-subscribing iff there are newer messages" do
203+
sender = user
204+
flip_flopper_guy = user
205+
subscription_guy = user
206+
archive_guy = user
207+
root_convo = Conversation.initiate([sender.id, flip_flopper_guy.id, archive_guy.id, subscription_guy.id], false)
208+
root_convo.add_message(sender, 'test')
209+
210+
flip_flopper_guy.conversations.first.update_attributes(:subscribed => false)
211+
flip_flopper_guy.reload.unread_conversations_count.should eql 0
212+
flip_flopper_guy.conversations.unread.size.should eql 0
213+
# no new messages in the interim, he should stay "marked-as-read"
214+
flip_flopper_guy.conversations.first.update_attributes(:subscribed => true)
215+
flip_flopper_guy.reload.unread_conversations_count.should eql 0
216+
flip_flopper_guy.conversations.unread.size.should eql 0
217+
218+
subscription_guy.conversations.first.update_attributes(:subscribed => false)
219+
archive_guy.conversations.first.archive!
220+
archive_guy.conversations.first.update_attributes(:subscribed => false)
221+
222+
message = root_convo.add_message(sender, 'you wish you were subscribed!')
223+
message.update_attribute(:created_at, Time.now.utc + 1.minute)
224+
last_message_at = message.reload.created_at
225+
226+
subscription_guy.conversations.first.update_attributes(:subscribed => true)
227+
archive_guy.conversations.first.update_attributes(:subscribed => true)
228+
229+
subscription_guy.reload.unread_conversations_count.should eql 1
230+
subscription_guy.conversations.unread.size.should eql 1
231+
subscription_guy.conversations.first.last_message_at.should eql last_message_at
232+
233+
archive_guy.reload.unread_conversations_count.should eql 1
234+
archive_guy.conversations.unread.size.should eql 1
235+
subscription_guy.conversations.first.last_message_at.should eql last_message_at
236+
end
237+
238+
it "should not toggle read/unread until the subscription change is saved" do
239+
sender = user
240+
subscription_guy = user
241+
root_convo = Conversation.initiate([sender.id, user.id, subscription_guy.id], false)
242+
root_convo.add_message(sender, 'test')
243+
244+
subscription_guy.reload.unread_conversations_count.should eql 1
245+
subscription_guy.conversations.unread.size.should eql 1
246+
247+
subscription_guy.conversations.first.subscribed = false
248+
subscription_guy.reload.unread_conversations_count.should eql 1
249+
subscription_guy.conversations.unread.size.should eql 1
250+
251+
subscription_guy.conversations.first.subscribed = true
252+
subscription_guy.reload.unread_conversations_count.should eql 1
253+
subscription_guy.conversations.unread.size.should eql 1
254+
end
255+
end
256+
181257
context "adding messages" do
182258
it "should deliver the message to all participants" do
183259
sender = user
@@ -195,14 +271,30 @@
195271
end
196272
end
197273

198-
it "should not auto mark it as read for the sender" do
274+
it "should never change the workflow_state for the sender" do
199275
sender = user
200-
recipients = 5.times.map{ user }
201-
Conversation.initiate([sender.id] + recipients.map(&:id), false).add_message(sender, 'test')
276+
Conversation.initiate([sender.id, user.id], true).add_message(sender, 'test')
202277
convo = sender.conversations.first
203278
convo.mark_as_unread!
204279
convo.add_message('another test')
205280
convo.reload.unread?.should be_true
281+
282+
convo.archive!
283+
convo.add_message('one more test')
284+
convo.reload.archived?.should be_true
285+
286+
convo.unarchive!
287+
convo.mark_as_unread!
288+
convo.add_message('and another test', :update_for_sender => true) # overrides subscribed-ness and updates timestamps
289+
convo.reload.unread?.should be_true
290+
291+
convo.archive!
292+
convo.add_message('last one', :update_for_sender => true)
293+
convo.reload.archived?.should be_true
294+
295+
convo.remove_messages(:all)
296+
convo.add_message('for reals', :update_for_sender => true)
297+
convo.reload.archived?.should be_true
206298
end
207299

208300
it "should deliver the message to unsubscribed participants but not alert them" do
@@ -217,7 +309,8 @@
217309
rconvo.unread?.should be_false
218310

219311
convo = sender.conversations.first
220-
convo.add_message('another test')
312+
message = convo.add_message('another test')
313+
message.update_attribute(:created_at, Time.now.utc + 1.minute)
221314

222315
rconvo.reload.unread?.should be_false
223316
rconvo.update_attributes(:subscribed => true)

0 commit comments

Comments
 (0)