DEV: glimmerify discourse-presence (#29235)

This commit is contained in:
Régis Hanol
2024-10-17 14:51:26 +02:00
committed by GitHub
parent 7b8ebebe93
commit f05b984208
4 changed files with 206 additions and 160 deletions

View File

@ -1,23 +1,30 @@
{{#if this.shouldDisplay}} <div
<div class="presence-users"> {{did-insert this.setupChannels}}
<div class="presence-avatars"> {{did-update this.setupChannels @model.reply @model.whisper this.state}}
{{#each this.presenceUsers as |user|}} >
{{avatar user imageSize="small"}} {{#if this.shouldDisplay}}
{{/each}} <div class="presence-users">
<div class="presence-avatars">
{{#each this.users as |user|}}
<UserLink @user={{user}}>
{{avatar user imageSize="small"}}
</UserLink>
{{/each}}
</div>
<span class="presence-text">
<span class="description">
{{~#if this.isReply~}}
{{i18n "presence.replying" count=this.users.length}}
{{~else~}}
{{i18n "presence.editing" count=this.users.length}}
{{~/if~}}
</span>
<span class="wave">
<span class="dot">.</span>
<span class="dot">.</span>
<span class="dot">.</span>
</span>
</span>
</div> </div>
<span class="presence-text"> {{/if}}
<span class="description"> </div>
{{~#if this.isReply~}}
{{i18n "presence.replying" count=this.presenceUsers.length}}
{{~else~}}
{{i18n "presence.editing" count=this.presenceUsers.length}}
{{~/if~}}
</span>
<span class="wave">
<span class="dot">.</span>
<span class="dot">.</span>
<span class="dot">.</span>
</span>
</span>
</div>
{{/if}}

View File

@ -1,32 +1,30 @@
import Component from "@ember/component"; import Component from "@glimmer/component";
import { equal, gt, readOnly, union } from "@ember/object/computed"; import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { tagName } from "@ember-decorators/component";
import { observes, on } from "@ember-decorators/object";
import discourseComputed from "discourse-common/utils/decorators";
@tagName("") export default class ComposerPresenceDisplayComponent extends Component {
export default class ComposerPresenceDisplay extends Component {
@service presence; @service presence;
@service composerPresenceManager; @service composerPresenceManager;
@service currentUser;
@service siteSettings;
@equal("state", "reply") isReply; @tracked replyChannel;
@equal("state", "edit") isEdit; @tracked whisperChannel;
@equal("state", "whisper") isWhisper; @tracked editChannel;
@union("replyChannel.users", "whisperChannel.users") replyingUsers;
@readOnly("editChannel.users") editingUsers;
@gt("presenceUsers.length", 0) shouldDisplay;
@discourseComputed( get isReply() {
"model.replyingToTopic", return this.state === "reply" || this.state === "whisper";
"model.editingPost", }
"model.whisper",
"model.composerOpened" get isEdit() {
) return this.state === "edit";
state(replyingToTopic, editingPost, whisper, composerOpen) { }
if (!composerOpen) {
return; get state() {
} else if (editingPost) { const { editingPost, whisper, replyingToTopic } = this.args.model;
if (editingPost) {
return "edit"; return "edit";
} else if (whisper) { } else if (whisper) {
return "whisper"; return "whisper";
@ -35,77 +33,105 @@ export default class ComposerPresenceDisplay extends Component {
} }
} }
@discourseComputed("model.topic.id", "isReply", "isWhisper") get replyChannelName() {
replyChannelName(topicId, isReply, isWhisper) { const topicId = this.args.model?.topic?.id;
if (topicId && (isReply || isWhisper)) { if (topicId && this.isReply) {
return `/discourse-presence/reply/${topicId}`; return `/discourse-presence/reply/${topicId}`;
} }
} }
@discourseComputed("model.topic.id", "isReply", "isWhisper") get whisperChannelName() {
whisperChannelName(topicId, isReply, isWhisper) { const topicId = this.args.model?.topic?.id;
if (topicId && this.currentUser.whisperer && (isReply || isWhisper)) { if (topicId && this.isReply && this.currentUser.whisperer) {
return `/discourse-presence/whisper/${topicId}`; return `/discourse-presence/whisper/${topicId}`;
} }
} }
@discourseComputed("isEdit", "model.post.id") get editChannelName() {
editChannelName(isEdit, postId) { const postId = this.args.model?.post?.id;
if (isEdit) { if (postId && this.isEdit) {
return `/discourse-presence/edit/${postId}`; return `/discourse-presence/edit/${postId}`;
} }
} }
_setupChannel(channelKey, name) { get replyUsers() {
if (this[channelKey]?.name !== name) { return this.replyChannel?.users || [];
this[channelKey]?.unsubscribe(); }
get whisperUsers() {
return this.whisperChannel?.users || [];
}
get replyingUsers() {
return [...this.replyUsers, ...this.whisperUsers];
}
get editingUsers() {
return this.editChannel?.users || [];
}
get users() {
const users = this.isEdit ? this.editingUsers : this.replyingUsers;
return users
.filter((u) => u.id !== this.currentUser.id)
.slice(0, this.siteSettings.presence_max_users_shown);
}
get shouldDisplay() {
return this.users.length > 0;
}
@action
setupChannels() {
this.setupReplyChannel();
this.setupWhisperChannel();
this.setupEditChannel();
this.notifyState();
}
setupReplyChannel() {
this.setupChannel("replyChannel", this.replyChannelName);
}
setupWhisperChannel() {
if (this.currentUser.staff) {
this.setupChannel("whisperChannel", this.whisperChannelName);
}
}
setupEditChannel() {
this.setupChannel("editChannel", this.editChannelName);
}
setupChannel(key, name) {
if (this[key]?.name !== name) {
this[key]?.unsubscribe();
if (name) { if (name) {
this.set(channelKey, this.presence.getChannel(name)); this[key] = this.presence.getChannel(name);
this[channelKey].subscribe(); this[key].subscribe();
} else if (this[channelKey]) {
this.set(channelKey, null);
} }
} }
} }
@observes("replyChannelName", "whisperChannelName", "editChannelName") notifyState() {
_setupChannels() { const { reply, post, topic } = this.args.model;
this._setupChannel("replyChannel", this.replyChannelName); const raw = this.isEdit ? post?.raw || "" : "";
this._setupChannel("whisperChannel", this.whisperChannelName); const entity = this.isEdit ? post : topic;
this._setupChannel("editChannel", this.editChannelName);
}
_cleanupChannels() { if (reply !== raw) {
this._setupChannel("replyChannel", null); this.composerPresenceManager.notifyState(this.state, entity?.id);
this._setupChannel("whisperChannel", null);
this._setupChannel("editChannel", null);
}
@discourseComputed("isReply", "replyingUsers.[]", "editingUsers.[]")
presenceUsers(isReply, replyingUsers, editingUsers) {
const users = isReply ? replyingUsers : editingUsers;
return users
?.filter((u) => u.id !== this.currentUser.id)
?.slice(0, this.siteSettings.presence_max_users_shown);
}
@on("didInsertElement")
subscribe() {
this._setupChannels();
}
@observes("model.reply", "state", "model.post.id", "model.topic.id")
_contentChanged() {
if (this.model.reply === "") {
return;
} }
const entity = this.state === "edit" ? this.model?.post : this.model?.topic;
this.composerPresenceManager.notifyState(this.state, entity?.id);
} }
@on("willDestroyElement") willDestroy() {
closeComposer() { super.willDestroy(...arguments);
this._cleanupChannels(); this.unsubscribeFromChannels();
this.composerPresenceManager.leave(); this.composerPresenceManager.leave();
} }
unsubscribeFromChannels() {
this.replyChannel?.unsubscribe();
this.whisperChannel?.unsubscribe();
this.editChannel?.unsubscribe();
}
} }

View File

@ -1,21 +1,26 @@
{{#if this.shouldDisplay}} <div
<div class="presence-users"> {{did-insert this.setupChannels}}
<div class="presence-avatars"> {{did-update this.setupChannels @topic.id}}
{{#each this.users as |user|}} >
<UserLink @user={{user}}> {{#if this.shouldDisplay}}
{{avatar user imageSize="small"}} <div class="presence-users">
</UserLink> <div class="presence-avatars">
{{/each}} {{#each this.users as |user|}}
<UserLink @user={{user}}>
{{avatar user imageSize="small"}}
</UserLink>
{{/each}}
</div>
<span class="presence-text">
<span class="description">
{{i18n "presence.replying_to_topic" count=this.users.length}}
</span>
<span class="wave">
<span class="dot">.</span>
<span class="dot">.</span>
<span class="dot">.</span>
</span>
</span>
</div> </div>
<span class="presence-text"> {{/if}}
<span class="description"> </div>
{{i18n "presence.replying_to_topic" count=this.users.length}}
</span>
<span class="wave">
<span class="dot">.</span>
<span class="dot">.</span>
<span class="dot">.</span>
</span>
</span>
</div>
{{/if}}

View File

@ -1,64 +1,72 @@
import Component from "@ember/component"; import Component from "@glimmer/component";
import { gt, union } from "@ember/object/computed"; import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { on } from "@ember-decorators/object";
import discourseComputed from "discourse-common/utils/decorators";
export default class TopicPresenceDisplay extends Component { export default class TopicPresenceDisplayComponent extends Component {
@service presence; @service presence;
@service currentUser;
topic = null; @tracked replyChannel;
replyChannel = null; @tracked whisperChannel;
whisperChannel = null;
@union("replyUsers", "whisperUsers") users; get replyChannelName() {
@gt("users.length", 0) shouldDisplay; return `/discourse-presence/reply/${this.args.topic.id}`;
@discourseComputed("replyChannel.users.[]")
replyUsers(users) {
return users?.filter((u) => u.id !== this.currentUser.id);
} }
@discourseComputed("whisperChannel.users.[]") get whisperChannelName() {
whisperUsers(users) { return `/discourse-presence/whisper/${this.args.topic.id}`;
return users?.filter((u) => u.id !== this.currentUser.id);
} }
@discourseComputed("topic.id") get replyUsers() {
replyChannelName(id) { return this.replyChannel?.users || [];
return `/discourse-presence/reply/${id}`;
} }
@discourseComputed("topic.id") get whisperUsers() {
whisperChannelName(id) { return this.whisperChannel?.users || [];
return `/discourse-presence/whisper/${id}`;
} }
didReceiveAttrs() { get users() {
super.didReceiveAttrs(...arguments); return [...this.replyUsers, ...this.whisperUsers].filter(
(u) => u.id !== this.currentUser.id
if (this.replyChannel?.name !== this.replyChannelName) { );
this.replyChannel?.unsubscribe();
this.set("replyChannel", this.presence.getChannel(this.replyChannelName));
this.replyChannel.subscribe();
}
if (
this.currentUser.staff &&
this.whisperChannel?.name !== this.whisperChannelName
) {
this.whisperChannel?.unsubscribe();
this.set(
"whisperChannel",
this.presence.getChannel(this.whisperChannelName)
);
this.whisperChannel.subscribe();
}
} }
@on("willDestroyElement") get shouldDisplay() {
_destroyed() { return this.users.length > 0;
}
@action
setupChannels() {
this.setupReplyChannel();
this.setupWhisperChannel();
}
willDestroy() {
super.willDestroy(...arguments);
this.unsubscribeFromChannels();
}
unsubscribeFromChannels() {
this.replyChannel?.unsubscribe(); this.replyChannel?.unsubscribe();
this.whisperChannel?.unsubscribe(); this.whisperChannel?.unsubscribe();
} }
setupReplyChannel() {
if (this.replyChannel?.name !== this.replyChannelName) {
this.replyChannel?.unsubscribe();
this.replyChannel = this.presence.getChannel(this.replyChannelName);
this.replyChannel.subscribe();
}
}
setupWhisperChannel() {
if (this.currentUser.staff) {
if (this.whisperChannel?.name !== this.whisperChannelName) {
this.whisperChannel?.unsubscribe();
this.whisperChannel = this.presence.getChannel(this.whisperChannelName);
this.whisperChannel.subscribe();
}
}
}
} }