FEATURE: Track last_viewed_at datetime for channel members (#22294)

Whenever a user opens a channel or marks it read, we now
update the last_viewed_at datetime for that channel membership
record. This is so we will be able to show thread unread indicators
in the channel sidebar that clear independently of the main thread
unread indicators. This unread functionality will follow in another
PR.
This commit is contained in:
Martin Brennan
2023-06-29 09:22:17 +10:00
committed by GitHub
parent 58c14ba0f9
commit 1194ed10e1
6 changed files with 40 additions and 4 deletions

View File

@ -32,6 +32,7 @@ end
# updated_at :datetime not null # updated_at :datetime not null
# last_unread_mention_when_emailed_id :integer # last_unread_mention_when_emailed_id :integer
# join_mode :integer default("manual"), not null # join_mode :integer default("manual"), not null
# last_viewed_at :datetime not null
# #
# Indexes # Indexes
# #

View File

@ -39,6 +39,7 @@ module Chat
step :fetch_tracking step :fetch_tracking
step :fetch_thread_memberships step :fetch_thread_memberships
step :fetch_thread_participants step :fetch_thread_participants
step :update_channel_last_viewed_at
step :build_view step :build_view
class Contract class Contract
@ -226,6 +227,10 @@ module Chat
::Chat::ThreadParticipantQuery.call(thread_ids: threads.map(&:id)) ::Chat::ThreadParticipantQuery.call(thread_ids: threads.map(&:id))
end end
def update_channel_last_viewed_at(channel:, guardian:, **)
channel.membership_for(guardian.user)&.update!(last_viewed_at: Time.zone.now)
end
def build_view( def build_view(
guardian:, guardian:,
channel:, channel:,

View File

@ -21,8 +21,10 @@ module Chat
policy :invalid_access policy :invalid_access
model :message model :message
policy :ensure_message_id_recency policy :ensure_message_id_recency
step :update_last_read_message_id transaction do
step :mark_associated_mentions_as_read step :update_membership_state
step :mark_associated_mentions_as_read
end
step :publish_new_last_read_to_clients step :publish_new_last_read_to_clients
# @!visibility private # @!visibility private
@ -56,8 +58,8 @@ module Chat
message.id >= active_membership.last_read_message_id message.id >= active_membership.last_read_message_id
end end
def update_last_read_message_id(message:, active_membership:, **) def update_membership_state(message:, active_membership:, **)
active_membership.update!(last_read_message_id: message.id) active_membership.update!(last_read_message_id: message.id, last_viewed_at: Time.zone.now)
end end
def mark_associated_mentions_as_read(active_membership:, message:, **) def mark_associated_mentions_as_read(active_membership:, message:, **)

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
class AddLastViewedAtToUserChatChannelMemberships < ActiveRecord::Migration[7.0]
def change
add_column :user_chat_channel_memberships,
:last_viewed_at,
:datetime,
null: false,
default: -> { "CURRENT_TIMESTAMP" }
end
end

View File

@ -37,6 +37,8 @@ RSpec.describe Chat::ChannelViewBuilder do
} }
end end
before { channel.add(current_user) }
it "threads_enabled is false by default" do it "threads_enabled is false by default" do
expect(result.threads_enabled).to eq(false) expect(result.threads_enabled).to eq(false)
end end
@ -76,6 +78,14 @@ RSpec.describe Chat::ChannelViewBuilder do
) )
end end
it "updates the channel membership last_viewed_at" do
membership = channel.membership_for(current_user)
membership.update!(last_viewed_at: 1.day.ago)
old_last_viewed_at = membership.last_viewed_at
result
expect(membership.reload.last_viewed_at).not_to eq_time(old_last_viewed_at)
end
it "does not query thread tracking overview or state by default" do it "does not query thread tracking overview or state by default" do
Chat::TrackingStateReportQuery.expects(:call).never Chat::TrackingStateReportQuery.expects(:call).never
result result

View File

@ -109,6 +109,13 @@ RSpec.describe Chat::UpdateUserLastRead do
it "publishes new last read to clients" do it "publishes new last read to clients" do
expect(messages.map(&:channel)).to include("/chat/user-tracking-state/#{current_user.id}") expect(messages.map(&:channel)).to include("/chat/user-tracking-state/#{current_user.id}")
end end
it "updates the channel membership last_viewed_at datetime" do
membership.update!(last_viewed_at: 1.day.ago)
old_last_viewed_at = membership.last_viewed_at
result
expect(membership.reload.last_viewed_at).not_to eq_time(old_last_viewed_at)
end
end end
end end
end end