mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
This moves chat tracking state calculation for channels and threads into a central Chat::TrackingStateManager service, that serves a similar purpose to the TopicTrackingState model in core. This service calls down to these query classes: * ThreadUnreadsQuery * ChannelUnreadsQuery To get the unread_count and mention_count for the appropriate channels and threads. As well as this, this commit refactors the client-side chat tracking state. Now, there is a central ChatTrackingStateManager Ember Service so all tracking is accessible and can be counted from one place, which can also initialize tracking from an initial payload. The actual tracking counts are now maintained in a ChatTrackingState class that is initialized on the `.tracking` property of both channel and thread objects. This removes the attributes on UserChatChannelMembership and decoration of said membership from ChannelFetcher, preferring instead to have an additional object for tracking in the JSON.
152 lines
4.1 KiB
JavaScript
152 lines
4.1 KiB
JavaScript
import Service, { inject as service } from "@ember/service";
|
|
import { debounce } from "discourse-common/utils/decorators";
|
|
import Promise from "rsvp";
|
|
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
|
import { tracked } from "@glimmer/tracking";
|
|
import { TrackedObject } from "@ember-compat/tracked-built-ins";
|
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
|
|
const DIRECT_MESSAGE_CHANNELS_LIMIT = 20;
|
|
|
|
/*
|
|
The ChatChannelsManager service is responsible for managing the loaded chat channels.
|
|
It provides helpers to facilitate using and managing laoded channels instead of constantly
|
|
fetching them from the server.
|
|
*/
|
|
|
|
export default class ChatChannelsManager extends Service {
|
|
@service chatSubscriptionsManager;
|
|
@service chatApi;
|
|
@service currentUser;
|
|
@tracked _cached = new TrackedObject();
|
|
|
|
async find(id, options = { fetchIfNotFound: true }) {
|
|
const existingChannel = this.#findStale(id);
|
|
if (existingChannel) {
|
|
return Promise.resolve(existingChannel);
|
|
} else if (options.fetchIfNotFound) {
|
|
return this.#find(id);
|
|
} else {
|
|
return Promise.resolve();
|
|
}
|
|
}
|
|
|
|
get channels() {
|
|
return Object.values(this._cached);
|
|
}
|
|
|
|
store(channelObject) {
|
|
let model = this.#findStale(channelObject.id);
|
|
|
|
if (!model) {
|
|
model = ChatChannel.create(channelObject);
|
|
this.#cache(model);
|
|
}
|
|
|
|
if (
|
|
channelObject.meta?.message_bus_last_ids?.channel_message_bus_last_id !==
|
|
undefined
|
|
) {
|
|
model.channelMessageBusLastId =
|
|
channelObject.meta.message_bus_last_ids.channel_message_bus_last_id;
|
|
}
|
|
|
|
return model;
|
|
}
|
|
|
|
async follow(model) {
|
|
this.chatSubscriptionsManager.startChannelSubscription(model);
|
|
|
|
if (!model.currentUserMembership.following) {
|
|
return this.chatApi.followChannel(model.id).then((membership) => {
|
|
model.currentUserMembership.following = membership.following;
|
|
model.currentUserMembership.muted = membership.muted;
|
|
model.currentUserMembership.desktopNotificationLevel =
|
|
membership.desktopNotificationLevel;
|
|
model.currentUserMembership.mobileNotificationLevel =
|
|
membership.mobileNotificationLevel;
|
|
return model;
|
|
});
|
|
} else {
|
|
return Promise.resolve(model);
|
|
}
|
|
}
|
|
|
|
async unfollow(model) {
|
|
this.chatSubscriptionsManager.stopChannelSubscription(model);
|
|
|
|
return this.chatApi.unfollowChannel(model.id).then((membership) => {
|
|
model.currentUserMembership = membership;
|
|
|
|
return model;
|
|
});
|
|
}
|
|
|
|
@debounce(300)
|
|
async markAllChannelsRead() {
|
|
// The user tracking state for each channel marked read will be propagated by MessageBus
|
|
return this.chatApi.markAllChannelsAsRead();
|
|
}
|
|
|
|
remove(model) {
|
|
this.chatSubscriptionsManager.stopChannelSubscription(model);
|
|
delete this._cached[model.id];
|
|
}
|
|
|
|
get publicMessageChannels() {
|
|
return this.channels
|
|
.filter(
|
|
(channel) =>
|
|
channel.isCategoryChannel && channel.currentUserMembership.following
|
|
)
|
|
.sort((a, b) => a?.slug?.localeCompare?.(b?.slug));
|
|
}
|
|
|
|
get directMessageChannels() {
|
|
return this.#sortDirectMessageChannels(
|
|
this.channels.filter((channel) => {
|
|
const membership = channel.currentUserMembership;
|
|
return channel.isDirectMessageChannel && membership.following;
|
|
})
|
|
);
|
|
}
|
|
|
|
get truncatedDirectMessageChannels() {
|
|
return this.directMessageChannels.slice(0, DIRECT_MESSAGE_CHANNELS_LIMIT);
|
|
}
|
|
|
|
async #find(id) {
|
|
return this.chatApi
|
|
.channel(id)
|
|
.catch(popupAjaxError)
|
|
.then((channel) => {
|
|
this.#cache(channel);
|
|
return channel;
|
|
});
|
|
}
|
|
|
|
#cache(channel) {
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
|
|
this._cached[channel.id] = channel;
|
|
}
|
|
|
|
#findStale(id) {
|
|
return this._cached[id];
|
|
}
|
|
|
|
#sortDirectMessageChannels(channels) {
|
|
return channels.sort((a, b) => {
|
|
if (a.tracking.unreadCount === b.tracking.unreadCount) {
|
|
return new Date(a.lastMessageSentAt) > new Date(b.lastMessageSentAt)
|
|
? -1
|
|
: 1;
|
|
} else {
|
|
return a.tracking.unreadCount > b.tracking.unreadCount ? -1 : 1;
|
|
}
|
|
});
|
|
}
|
|
}
|