mirror of
https://github.com/hanzoai/forum.git
synced 2026-09-30 21:51:19 +00:00
`discourse-common` was created in the past to share logic between the 'wizard' app and the main 'discourse' app. Since then, the wizard has been consolidated into the main app, so the separation of `discourse-common` is no longer useful. This commit moves `discourse-common/(lib|utils)/*` into `discourse/lib/*`, adds shims for the imports, and updates existing uses in core.
300 lines
7.8 KiB
JavaScript
300 lines
7.8 KiB
JavaScript
import { cached, tracked } from "@glimmer/tracking";
|
|
import Service, { service } from "@ember/service";
|
|
import { TrackedObject } from "@ember-compat/tracked-built-ins";
|
|
import Promise from "rsvp";
|
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
import { debounce } from "discourse/lib/decorators";
|
|
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
|
|
|
const DIRECT_MESSAGE_CHANNELS_LIMIT = 50;
|
|
|
|
/*
|
|
The ChatChannelsManager service is responsible for managing the loaded chat channels.
|
|
It provides helpers to facilitate using and managing loaded channels instead of constantly
|
|
fetching them from the server.
|
|
*/
|
|
|
|
export default class ChatChannelsManager extends Service {
|
|
@service chatApi;
|
|
@service chatSubscriptionsManager;
|
|
@service chatStateManager;
|
|
@service currentUser;
|
|
@service router;
|
|
@service site;
|
|
@service siteSettings;
|
|
@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();
|
|
}
|
|
}
|
|
|
|
@cached
|
|
get channels() {
|
|
return Object.values(this._cached);
|
|
}
|
|
|
|
store(channelObject, options = {}) {
|
|
let model;
|
|
|
|
if (!options.replace) {
|
|
model = this.#findStale(channelObject.id);
|
|
}
|
|
|
|
if (!model) {
|
|
if (channelObject instanceof ChatChannel) {
|
|
model = channelObject;
|
|
} else {
|
|
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 = membership;
|
|
return model;
|
|
});
|
|
} else {
|
|
return model;
|
|
}
|
|
}
|
|
|
|
async unfollow(model) {
|
|
try {
|
|
this.chatSubscriptionsManager.stopChannelSubscription(model);
|
|
model.currentUserMembership = await this.chatApi.unfollowChannel(
|
|
model.id
|
|
);
|
|
return model;
|
|
} catch (error) {
|
|
popupAjaxError(error);
|
|
}
|
|
}
|
|
|
|
@debounce(300)
|
|
async markAllChannelsRead() {
|
|
// The user tracking state for each channel marked read will be propagated by MessageBus
|
|
return this.chatApi.markAllChannelsAsRead();
|
|
}
|
|
|
|
remove(model) {
|
|
if (!model) {
|
|
return;
|
|
}
|
|
this.chatSubscriptionsManager.stopChannelSubscription(model);
|
|
delete this._cached[model.id];
|
|
}
|
|
|
|
@cached
|
|
get hasThreadedChannels() {
|
|
return this.allChannels?.some((channel) => channel.threadingEnabled);
|
|
}
|
|
|
|
get allChannels() {
|
|
return [...this.publicMessageChannels, ...this.directMessageChannels].sort(
|
|
(a, b) => {
|
|
return b?.currentUserMembership?.lastViewedAt?.localeCompare?.(
|
|
a?.currentUserMembership?.lastViewedAt
|
|
);
|
|
}
|
|
);
|
|
}
|
|
|
|
@cached
|
|
get publicMessageChannels() {
|
|
return this.channels
|
|
.filter(
|
|
(channel) =>
|
|
channel.isCategoryChannel && channel.currentUserMembership.following
|
|
)
|
|
.sort((a, b) => a?.slug?.localeCompare?.(b?.slug));
|
|
}
|
|
|
|
get publicMessageChannelsWithActivity() {
|
|
return this.publicMessageChannels.filter((channel) => channel.hasUnread);
|
|
}
|
|
|
|
get publicMessageChannelsByActivity() {
|
|
return this.#sortChannelsByActivity([...this.publicMessageChannels]);
|
|
}
|
|
|
|
@cached
|
|
get directMessageChannels() {
|
|
return this.#sortDirectMessageChannels(
|
|
this.channels.filter((channel) => {
|
|
const membership = channel.currentUserMembership;
|
|
return channel.isDirectMessageChannel && membership.following;
|
|
})
|
|
);
|
|
}
|
|
|
|
get directMessageChannelsWithActivity() {
|
|
return this.directMessageChannels.filter((channel) => channel.hasUnread);
|
|
}
|
|
|
|
get truncatedDirectMessageChannels() {
|
|
return this.directMessageChannels.slice(0, DIRECT_MESSAGE_CHANNELS_LIMIT);
|
|
}
|
|
|
|
async #find(id) {
|
|
try {
|
|
const result = await this.chatApi.channel(id);
|
|
return this.store(result.channel);
|
|
} catch (error) {
|
|
popupAjaxError(error);
|
|
}
|
|
}
|
|
|
|
get publicMessageChannelsEmpty() {
|
|
return (
|
|
this.publicMessageChannels?.length === 0 &&
|
|
this.chatStateManager.hasPreloadedChannels
|
|
);
|
|
}
|
|
|
|
get displayPublicChannels() {
|
|
if (!this.siteSettings.enable_public_channels) {
|
|
return false;
|
|
}
|
|
|
|
if (!this.chatStateManager.hasPreloadedChannels) {
|
|
return false;
|
|
}
|
|
|
|
if (this.publicMessageChannelsEmpty) {
|
|
return (
|
|
this.currentUser?.staff ||
|
|
this.currentUser?.has_joinable_public_channels
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#cache(channel) {
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
|
|
this._cached[channel.id] = channel;
|
|
}
|
|
|
|
#findStale(id) {
|
|
return this._cached[id];
|
|
}
|
|
|
|
#sortChannelsByActivity(channels) {
|
|
return channels.sort((a, b) => {
|
|
const stats = {
|
|
a: {
|
|
urgent:
|
|
a.tracking.mentionCount + a.tracking.watchedThreadsUnreadCount,
|
|
unread: a.tracking.unreadCount + a.unreadThreadsCountSinceLastViewed,
|
|
},
|
|
b: {
|
|
urgent:
|
|
b.tracking.mentionCount + b.tracking.watchedThreadsUnreadCount,
|
|
unread: b.tracking.unreadCount + b.unreadThreadsCountSinceLastViewed,
|
|
},
|
|
};
|
|
|
|
// if both channels have urgent count, sort by slug
|
|
// otherwise prioritize channel with urgent count
|
|
if (stats.a.urgent > 0 && stats.b.urgent > 0) {
|
|
return a.slug?.localeCompare?.(b.slug);
|
|
}
|
|
|
|
if (stats.a.urgent > 0 || stats.b.urgent > 0) {
|
|
return stats.a.urgent > stats.b.urgent ? -1 : 1;
|
|
}
|
|
|
|
// if both channels have unread messages or threads, sort by slug
|
|
// otherwise prioritize channel with unread count
|
|
if (stats.a.unread > 0 && stats.b.unread > 0) {
|
|
return a.slug?.localeCompare?.(b.slug);
|
|
}
|
|
|
|
if (stats.a.unread > 0 || stats.b.unread > 0) {
|
|
return stats.a.unread > stats.b.unread ? -1 : 1;
|
|
}
|
|
|
|
return a.slug?.localeCompare?.(b.slug);
|
|
});
|
|
}
|
|
|
|
#sortDirectMessageChannels(channels) {
|
|
return channels.sort((a, b) => {
|
|
if (!a.lastMessage.id) {
|
|
return 1;
|
|
}
|
|
|
|
if (!b.lastMessage.id) {
|
|
return -1;
|
|
}
|
|
|
|
const aUrgent =
|
|
a.tracking.unreadCount +
|
|
a.tracking.mentionCount +
|
|
a.tracking.watchedThreadsUnreadCount;
|
|
|
|
const bUrgent =
|
|
b.tracking.unreadCount +
|
|
b.tracking.mentionCount +
|
|
b.tracking.watchedThreadsUnreadCount;
|
|
|
|
const aUnread = a.unreadThreadsCountSinceLastViewed;
|
|
const bUnread = b.unreadThreadsCountSinceLastViewed;
|
|
|
|
// if both channels have urgent count, sort by last message date
|
|
if (aUrgent > 0 && bUrgent > 0) {
|
|
return new Date(a.lastMessage.createdAt) >
|
|
new Date(b.lastMessage.createdAt)
|
|
? -1
|
|
: 1;
|
|
}
|
|
|
|
// otherwise prioritize channel with urgent count
|
|
if (aUrgent > 0 || bUrgent > 0) {
|
|
return aUrgent > bUrgent ? -1 : 1;
|
|
}
|
|
|
|
// if both channels have unread threads, sort by last thread reply date
|
|
if (aUnread > 0 && bUnread > 0) {
|
|
return a.lastUnreadThreadDate > b.lastUnreadThreadDate ? -1 : 1;
|
|
}
|
|
|
|
// otherwise prioritize channel with unread thread count
|
|
if (aUnread > 0 || bUnread > 0) {
|
|
return aUnread > bUnread ? -1 : 1;
|
|
}
|
|
|
|
// read channels are sorted by last message date
|
|
return new Date(a.lastMessage.createdAt) >
|
|
new Date(b.lastMessage.createdAt)
|
|
? -1
|
|
: 1;
|
|
});
|
|
}
|
|
}
|