DEV: Move ChatThreadsManager to channel (#20304)

This commit changes the ChatThreadsManager into a native
class instead of an ember service, and initializes it
for every ChatChannel model. This way each channel has its
own thread manager and cache that we can load/unload as
needed, and we also move activeThread to the channel since
it makes more sense to keep it there, not inside the chat service.

The pattern of calling setOwner with the passed in owner
from ChatChannel is adapted from the latest ember docs,
and is needed to avoid the error below when calling services
from the native class:

> Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container

It works well _only_ if we use our own getOwner wrapper
from addon/lib/get-owner, which is for backwards compat.

c.f. https://guides.emberjs.com/release/in-depth-topics/native-classes-in-depth/
This commit is contained in:
Martin Brennan 2023-02-16 10:00:40 +10:00 committed by GitHub
parent 183946a549
commit c0a086a988
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 22 additions and 25 deletions

View File

@ -9,7 +9,7 @@ export default class ChatThreadPanel extends Component {
@service router; @service router;
get thread() { get thread() {
return this.chat.activeThread; return this.chat.activeChannel.activeThread;
} }
get title() { get title() {

View File

@ -1,4 +1,5 @@
import Service, { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { setOwner } from "@ember/application";
import Promise from "rsvp"; import Promise from "rsvp";
import ChatThread from "discourse/plugins/chat/discourse/models/chat-thread"; import ChatThread from "discourse/plugins/chat/discourse/models/chat-thread";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
@ -6,19 +7,23 @@ import { TrackedObject } from "@ember-compat/tracked-built-ins";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
/* /*
The ChatThreadsManager service is responsible for managing the loaded chat threads The ChatThreadsManager is responsible for managing the loaded chat threads
for the current chat channel. for a ChatChannel model.
It provides helpers to facilitate using and managing loaded threads instead of constantly It provides helpers to facilitate using and managing loaded threads instead of constantly
fetching them from the server. fetching them from the server.
*/ */
export default class ChatThreadsManager extends Service { export default class ChatThreadsManager {
@service chatSubscriptionsManager; @service chatSubscriptionsManager;
@service chatApi; @service chatApi;
@service currentUser; @service currentUser;
@tracked _cached = new TrackedObject(); @tracked _cached = new TrackedObject();
constructor(owner) {
setOwner(this, owner);
}
async find(channelId, threadId, options = { fetchIfNotFound: true }) { async find(channelId, threadId, options = { fetchIfNotFound: true }) {
const existingThread = this.#findStale(threadId); const existingThread = this.#findStale(threadId);
if (existingThread) { if (existingThread) {
@ -30,11 +35,6 @@ export default class ChatThreadsManager extends Service {
} }
} }
// whenever the active channel changes, do this
resetCache() {
this._cached = new TrackedObject();
}
get threads() { get threads() {
return Object.values(this._cached); return Object.values(this._cached);
} }

View File

@ -6,6 +6,8 @@ import { ajax } from "discourse/lib/ajax";
import { escapeExpression } from "discourse/lib/utilities"; import { escapeExpression } from "discourse/lib/utilities";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import slugifyChannel from "discourse/plugins/chat/discourse/lib/slugify-channel"; import slugifyChannel from "discourse/plugins/chat/discourse/lib/slugify-channel";
import ChatThreadsManager from "discourse/plugins/chat/discourse/lib/chat-threads-manager";
import { getOwner } from "discourse-common/lib/get-owner";
export const CHATABLE_TYPES = { export const CHATABLE_TYPES = {
directMessageChannel: "DirectMessage", directMessageChannel: "DirectMessage",
@ -65,6 +67,9 @@ export default class ChatChannel extends RestModel {
@tracked description; @tracked description;
@tracked chatableType; @tracked chatableType;
@tracked status; @tracked status;
@tracked activeThread;
threadsManager = new ChatThreadsManager(getOwner(this));
get escapedTitle() { get escapedTitle() {
return escapeExpression(this.title); return escapeExpression(this.title);

View File

@ -3,19 +3,16 @@ import { inject as service } from "@ember/service";
export default class ChatChannelThread extends DiscourseRoute { export default class ChatChannelThread extends DiscourseRoute {
@service router; @service router;
@service chatThreadsManager;
@service chatStateManager; @service chatStateManager;
@service chat; @service chat;
async model(params) { async model(params) {
return this.chatThreadsManager.find( const channel = this.modelFor("chat.channel");
this.modelFor("chat.channel").id, return channel.threadsManager.find(channel.id, params.threadId);
params.threadId
);
} }
afterModel(model) { afterModel(model) {
this.chat.activeThread = model; this.chat.activeChannel.activeThread = model;
this.chatStateManager.openSidePanel(); this.chatStateManager.openSidePanel();
} }
} }

View File

@ -5,12 +5,12 @@ import { action } from "@ember/object";
@withChatChannel @withChatChannel
export default class ChatChannelRoute extends DiscourseRoute { export default class ChatChannelRoute extends DiscourseRoute {
@service chatThreadsManager; @service chat;
@service chatStateManager; @service chatStateManager;
@action @action
willTransition(transition) { willTransition(transition) {
this.chat.activeThread = null; this.chat.activeChannel.activeThread = null;
this.chatStateManager.closeSidePanel(); this.chatStateManager.closeSidePanel();
if (!transition?.to?.name?.startsWith("chat.")) { if (!transition?.to?.name?.startsWith("chat.")) {
@ -19,8 +19,4 @@ export default class ChatChannelRoute extends DiscourseRoute {
this.chat.updatePresence(); this.chat.updatePresence();
} }
} }
beforeModel() {
this.chatThreadsManager.resetCache();
}
} }

View File

@ -10,8 +10,8 @@ import Collection from "../lib/collection";
* @implements {@ember/service} * @implements {@ember/service}
*/ */
export default class ChatApi extends Service { export default class ChatApi extends Service {
@service chat;
@service chatChannelsManager; @service chatChannelsManager;
@service chatThreadsManager;
/** /**
* Get a channel by its ID. * Get a channel by its ID.
@ -40,7 +40,7 @@ export default class ChatApi extends Service {
*/ */
thread(channelId, threadId) { thread(channelId, threadId) {
return this.#getRequest(`/channels/${channelId}/threads/${threadId}`).then( return this.#getRequest(`/channels/${channelId}/threads/${threadId}`).then(
(result) => this.chatThreadsManager.store(result.thread) (result) => this.chat.activeChannel.threadsManager.store(result.thread)
); );
} }

View File

@ -36,7 +36,6 @@ export default class Chat extends Service {
@service site; @service site;
@service chatChannelsManager; @service chatChannelsManager;
@tracked activeChannel = null; @tracked activeChannel = null;
@tracked activeThread = null;
cook = null; cook = null;
presenceChannel = null; presenceChannel = null;
sidebarActive = false; sidebarActive = false;