mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: converts models to native classes (#21418)
- `ChatChannel` - `UserChatChannelMembership` Also creates a new `chat-direct-message` model used as the object for the`chatable` property of the `ChatChannel` when the `ChatChannel` is a direct message channel. When the chatable is a category a real `Category` object will now be returned. Archive state of a `ChatChannel` is now hold in a `ChatChannelArchive` object.
This commit is contained in:
parent
afbeeea09f
commit
22521d3428
@ -42,7 +42,7 @@ class Chat::Api::ChatablesController < Chat::ApiController
|
|||||||
direct_message_channels =
|
direct_message_channels =
|
||||||
if users.count > 0
|
if users.count > 0
|
||||||
# FIXME: investigate the cost of this query
|
# FIXME: investigate the cost of this query
|
||||||
Chat::Channel
|
Chat::DirectMessageChannel
|
||||||
.includes(chatable: :users)
|
.includes(chatable: :users)
|
||||||
.joins(direct_message: :direct_message_users)
|
.joins(direct_message: :direct_message_users)
|
||||||
.group(1)
|
.group(1)
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
module Chat
|
module Chat
|
||||||
class DirectMessageSerializer < ApplicationSerializer
|
class DirectMessageSerializer < ApplicationSerializer
|
||||||
|
attributes :id
|
||||||
|
|
||||||
has_many :users, serializer: Chat::UserWithCustomFieldsAndStatusSerializer, embed: :objects
|
has_many :users, serializer: Chat::UserWithCustomFieldsAndStatusSerializer, embed: :objects
|
||||||
|
|
||||||
def users
|
def users
|
||||||
|
@ -40,7 +40,7 @@ export default Component.extend(ModalFunctionality, {
|
|||||||
.createChannelArchive(this.chatChannel.id, this._data())
|
.createChannelArchive(this.chatChannel.id, this._data())
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.flash(I18n.t("chat.channel_archive.process_started"), "success");
|
this.flash(I18n.t("chat.channel_archive.process_started"), "success");
|
||||||
this.chatChannel.set("status", CHANNEL_STATUSES.archived);
|
this.chatChannel.status = CHANNEL_STATUSES.archived;
|
||||||
|
|
||||||
discourseLater(() => {
|
discourseLater(() => {
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
{{#if (and this.channel.archive_failed this.currentUser.admin)}}
|
{{#if this.shouldRender}}
|
||||||
<div class="alert alert-warn chat-channel-retry-archive">
|
{{#if @channel.archive.failed}}
|
||||||
|
<div
|
||||||
|
class={{concat-class
|
||||||
|
"alert alert-warn chat-channel-retry-archive"
|
||||||
|
@channel.status
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div class="chat-channel-archive-failed-message">
|
<div class="chat-channel-archive-failed-message">
|
||||||
{{this.channelArchiveFailedMessage}}
|
{{this.channelArchiveFailedMessage}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="chat-channel-archive-failed-retry">
|
<div class="chat-channel-archive-failed-retry">
|
||||||
<DButton
|
<DButton
|
||||||
@action={{action "retryArchive"}}
|
@action={{this.retryArchive}}
|
||||||
@label="chat.channel_archive.retry"
|
@label="chat.channel_archive.retry"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else if (and this.channel.archive_completed this.currentUser.admin)}}
|
{{else if @channel.archive.completed}}
|
||||||
<div class="chat-channel-archive-status">
|
<div class={{concat-class "chat-channel-archive-status" @channel.status}}>
|
||||||
{{this.channelArchiveCompletedMessage}}
|
{{this.channelArchiveCompletedMessage}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/if}}
|
@ -1,61 +1,53 @@
|
|||||||
import Component from "@ember/component";
|
import Component from "@glimmer/component";
|
||||||
import { htmlSafe } from "@ember/template";
|
import { htmlSafe } from "@ember/template";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
|
import { isPresent } from "@ember/utils";
|
||||||
|
|
||||||
export default Component.extend({
|
export default class ChatChannelArchiveStatus extends Component {
|
||||||
channel: null,
|
@service chatApi;
|
||||||
tagName: "",
|
@service currentUser;
|
||||||
chatApi: service(),
|
|
||||||
|
|
||||||
@discourseComputed(
|
get shouldRender() {
|
||||||
"channel.status",
|
return this.currentUser.admin && isPresent(this.args.channel.archive);
|
||||||
"channel.archived_messages",
|
}
|
||||||
"channel.total_messages",
|
|
||||||
"channel.archive_failed"
|
get channelArchiveFailedMessage() {
|
||||||
)
|
const archive = this.args.channel.archive;
|
||||||
channelArchiveFailedMessage() {
|
const translationKey = !archive.topicId
|
||||||
const translationKey = !this.channel.archive_topic_id
|
|
||||||
? "chat.channel_status.archive_failed_no_topic"
|
? "chat.channel_status.archive_failed_no_topic"
|
||||||
: "chat.channel_status.archive_failed";
|
: "chat.channel_status.archive_failed";
|
||||||
return htmlSafe(
|
return htmlSafe(
|
||||||
I18n.t(translationKey, {
|
I18n.t(translationKey, {
|
||||||
completed: this.channel.archived_messages,
|
completed: archive.messages,
|
||||||
total: this.channel.total_messages,
|
total: archive.totalMessages,
|
||||||
topic_url: this._getTopicUrl(),
|
topic_url: this.#getTopicUrl(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed(
|
get channelArchiveCompletedMessage() {
|
||||||
"channel.status",
|
|
||||||
"channel.archived_messages",
|
|
||||||
"channel.total_messages",
|
|
||||||
"channel.archive_completed"
|
|
||||||
)
|
|
||||||
channelArchiveCompletedMessage() {
|
|
||||||
return htmlSafe(
|
return htmlSafe(
|
||||||
I18n.t("chat.channel_status.archive_completed", {
|
I18n.t("chat.channel_status.archive_completed", {
|
||||||
topic_url: this._getTopicUrl(),
|
topic_url: this.#getTopicUrl(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
retryArchive() {
|
retryArchive() {
|
||||||
return this.chatApi
|
return this.chatApi
|
||||||
.createChannelArchive(this.channel.id)
|
.createChannelArchive(this.args.channel.id)
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
},
|
}
|
||||||
|
|
||||||
_getTopicUrl() {
|
get #getTopicUrl() {
|
||||||
if (!this.channel.archive_topic_id) {
|
if (!this.args.channel.archive.topicId) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return getURL(`/t/-/${this.channel.archive_topic_id}`);
|
return getURL(`/t/-/${this.args.channel.archive.topicId}`);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{{#unless this.site.mobileView}}
|
{{#if this.shouldRender}}
|
||||||
<DButton
|
<DButton
|
||||||
@icon="times"
|
@icon="times"
|
||||||
@action={{this.onLeaveChannel}}
|
@action={{@onLeaveChannel}}
|
||||||
@class="btn-flat chat-channel-leave-btn"
|
@class="btn-flat chat-channel-leave-btn"
|
||||||
@title={{this.leaveChatTitleKey}}
|
@title={{this.leaveChatTitleKey}}
|
||||||
/>
|
/>
|
||||||
{{/unless}}
|
{{/if}}
|
@ -1,25 +1,19 @@
|
|||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import Component from "@glimmer/component";
|
||||||
import Component from "@ember/component";
|
|
||||||
import { equal } from "@ember/object/computed";
|
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import { CHATABLE_TYPES } from "discourse/plugins/chat/discourse/models/chat-channel";
|
import { isPresent } from "@ember/utils";
|
||||||
|
export default class ChatChannelLeaveBtn extends Component {
|
||||||
|
@service chat;
|
||||||
|
@service site;
|
||||||
|
|
||||||
export default Component.extend({
|
get shouldRender() {
|
||||||
tagName: "",
|
return !this.site.mobileView && isPresent(this.args.channel);
|
||||||
channel: null,
|
}
|
||||||
chat: service(),
|
|
||||||
|
|
||||||
isDirectMessageRow: equal(
|
get leaveChatTitleKey() {
|
||||||
"channel.chatable_type",
|
if (this.args.channel.isDirectMessageChannel) {
|
||||||
CHATABLE_TYPES.directMessageChannel
|
|
||||||
),
|
|
||||||
|
|
||||||
@discourseComputed("isDirectMessageRow")
|
|
||||||
leaveChatTitleKey(isDirectMessageRow) {
|
|
||||||
if (isDirectMessageRow) {
|
|
||||||
return "chat.direct_messages.leave";
|
return "chat.direct_messages.leave";
|
||||||
} else {
|
} else {
|
||||||
return "chat.leave";
|
return "chat.leave";
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
@ -19,7 +19,7 @@ export default class ChatChannelRow extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get channelHasUnread() {
|
get channelHasUnread() {
|
||||||
return this.args.channel.currentUserMembership.unread_count > 0;
|
return this.args.channel.currentUserMembership.unreadCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get #firstDirectMessageUser() {
|
get #firstDirectMessageUser() {
|
||||||
|
@ -5,9 +5,11 @@ import { action } from "@ember/object";
|
|||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "",
|
tagName: "",
|
||||||
|
|
||||||
@discourseComputed("model", "model.focused")
|
isFocused: false,
|
||||||
rowClassNames(model, focused) {
|
|
||||||
return `chat-channel-selection-row ${focused ? "focused" : ""} ${
|
@discourseComputed("model", "isFocused")
|
||||||
|
rowClassNames(model, isFocused) {
|
||||||
|
return `chat-channel-selection-row ${isFocused ? "focused" : ""} ${
|
||||||
this.model.user ? "user-row" : "channel-row"
|
this.model.user ? "user-row" : "channel-row"
|
||||||
}`;
|
}`;
|
||||||
},
|
},
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
<ConditionalLoadingSpinner @condition={{this.loading}}>
|
<ConditionalLoadingSpinner @condition={{this.loading}}>
|
||||||
{{#each this.channels as |channel|}}
|
{{#each this.channels as |channel|}}
|
||||||
<ChatChannelSelectionRow
|
<ChatChannelSelectionRow
|
||||||
|
@isFocused={{eq channel this.focusedRow}}
|
||||||
@model={{channel}}
|
@model={{channel}}
|
||||||
@onClick={{this.switchChannel}}
|
@onClick={{this.switchChannel}}
|
||||||
/>
|
/>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
import { schedule } from "@ember/runloop";
|
import { schedule } from "@ember/runloop";
|
||||||
@ -9,6 +8,8 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
|
|||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||||
import { isPresent } from "@ember/utils";
|
import { isPresent } from "@ember/utils";
|
||||||
|
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||||
|
import User from "discourse/models/user";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
chat: service(),
|
chat: service(),
|
||||||
@ -19,6 +20,7 @@ export default Component.extend({
|
|||||||
loading: false,
|
loading: false,
|
||||||
chatChannelsManager: service(),
|
chatChannelsManager: service(),
|
||||||
router: service(),
|
router: service(),
|
||||||
|
focusedRow: null,
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
@ -53,19 +55,16 @@ export default Component.extend({
|
|||||||
} else {
|
} else {
|
||||||
channel = this.channels.find((c) => c.user && c.id === id);
|
channel = this.channels.find((c) => c.user && c.id === id);
|
||||||
}
|
}
|
||||||
channel?.set("focused", true);
|
if (channel) {
|
||||||
this.channels.forEach((c) => {
|
this.set("focusedRow", channel);
|
||||||
if (c !== channel) {
|
|
||||||
c.set("focused", false);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
onKeyUp(e) {
|
onKeyUp(e) {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
let focusedChannel = this.channels.find((c) => c.focused);
|
let focusedChannel = this.channels.find((c) => c === this.focusedRow);
|
||||||
this.switchChannel(focusedChannel);
|
this.switchChannel(focusedChannel);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
} else if (e.key === "ArrowDown") {
|
} else if (e.key === "ArrowDown") {
|
||||||
@ -78,16 +77,17 @@ export default Component.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
arrowNavigateChannels(direction) {
|
arrowNavigateChannels(direction) {
|
||||||
const indexOfFocused = this.channels.findIndex((c) => c.focused);
|
const indexOfFocused = this.channels.findIndex(
|
||||||
|
(c) => c === this.focusedRow
|
||||||
|
);
|
||||||
if (indexOfFocused > -1) {
|
if (indexOfFocused > -1) {
|
||||||
const nextIndex = direction === "down" ? 1 : -1;
|
const nextIndex = direction === "down" ? 1 : -1;
|
||||||
const nextChannel = this.channels[indexOfFocused + nextIndex];
|
const nextChannel = this.channels[indexOfFocused + nextIndex];
|
||||||
if (nextChannel) {
|
if (nextChannel) {
|
||||||
this.channels[indexOfFocused].set("focused", false);
|
this.set("focusedRow", nextChannel);
|
||||||
nextChannel.set("focused", true);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.channels[0].set("focused", true);
|
this.set("focusedRow", this.channels[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule("afterRender", () => {
|
schedule("afterRender", () => {
|
||||||
@ -100,7 +100,7 @@ export default Component.extend({
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
switchChannel(channel) {
|
switchChannel(channel) {
|
||||||
if (channel.user) {
|
if (channel instanceof User) {
|
||||||
return this.fetchOrCreateChannelForUser(channel).then((response) => {
|
return this.fetchOrCreateChannelForUser(channel).then((response) => {
|
||||||
const newChannel = this.chatChannelsManager.store(response.channel);
|
const newChannel = this.chatChannelsManager.store(response.channel);
|
||||||
return this.chatChannelsManager.follow(newChannel).then((c) => {
|
return this.chatChannelsManager.follow(newChannel).then((c) => {
|
||||||
@ -145,21 +145,21 @@ export default Component.extend({
|
|||||||
.then((searchModel) => {
|
.then((searchModel) => {
|
||||||
if (this.searchIndex === thisSearchIndex) {
|
if (this.searchIndex === thisSearchIndex) {
|
||||||
this.set("searchModel", searchModel);
|
this.set("searchModel", searchModel);
|
||||||
const channels = searchModel.public_channels.concat(
|
let channels = searchModel.public_channels
|
||||||
searchModel.direct_message_channels,
|
.concat(searchModel.direct_message_channels, searchModel.users)
|
||||||
searchModel.users
|
.map((c) => {
|
||||||
);
|
if (
|
||||||
channels.forEach((c) => {
|
c.chatable_type === "DirectMessage" ||
|
||||||
if (c.username) {
|
c.chatable_type === "Category"
|
||||||
c.user = true; // This is used by the `chat-channel-selection-row` component
|
) {
|
||||||
|
return ChatChannel.create(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return User.create(c);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
channels: channels.map((channel) => {
|
channels,
|
||||||
return channel.user
|
|
||||||
? ChatChannel.create(channel)
|
|
||||||
: this.chatChannelsManager.store(channel);
|
|
||||||
}),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
this.focusFirstChannel(this.channels);
|
this.focusFirstChannel(this.channels);
|
||||||
@ -188,8 +188,11 @@ export default Component.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
focusFirstChannel(channels) {
|
focusFirstChannel(channels) {
|
||||||
channels.forEach((c) => c.set("focused", false));
|
if (channels[0]) {
|
||||||
channels[0]?.set("focused", true);
|
this.set("focusedRow", channels[0]);
|
||||||
|
} else {
|
||||||
|
this.set("focusedRow", null);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getChannelsWithFilter(filter, opts = { excludeActiveChannel: true }) {
|
getChannelsWithFilter(filter, opts = { excludeActiveChannel: true }) {
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
@value={{this.channel.currentUserMembership.muted}}
|
@value={{this.channel.currentUserMembership.muted}}
|
||||||
@valueProperty="value"
|
@valueProperty="value"
|
||||||
@class="channel-settings-view__muted-selector"
|
@class="channel-settings-view__muted-selector"
|
||||||
@onChange={{action (fn this.saveNotificationSettings "muted")}}
|
@onChange={{fn this.saveNotificationSettings "muted" "muted"}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -22,17 +22,19 @@
|
|||||||
<label class="chat-form__label">
|
<label class="chat-form__label">
|
||||||
<span>{{i18n "chat.settings.desktop_notification_level"}}</span>
|
<span>{{i18n "chat.settings.desktop_notification_level"}}</span>
|
||||||
<ChatChannelSettingsSavedIndicator
|
<ChatChannelSettingsSavedIndicator
|
||||||
@property={{this.channel.currentUserMembership.desktop_notification_level}}
|
@property={{this.channel.currentUserMembership.desktopNotificationLevel}}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div class="chat-form__control">
|
<div class="chat-form__control">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
@content={{this.notificationLevels}}
|
@content={{this.notificationLevels}}
|
||||||
@value={{this.channel.currentUserMembership.desktop_notification_level}}
|
@value={{this.channel.currentUserMembership.desktopNotificationLevel}}
|
||||||
@valueProperty="value"
|
@valueProperty="value"
|
||||||
@class="channel-settings-view__desktop-notification-level-selector"
|
@class="channel-settings-view__desktop-notification-level-selector"
|
||||||
@onChange={{action
|
@onChange={{fn
|
||||||
(fn this.saveNotificationSettings "desktop_notification_level")
|
this.saveNotificationSettings
|
||||||
|
"desktopNotificationLevel"
|
||||||
|
"desktop_notification_level"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -42,17 +44,19 @@
|
|||||||
<label class="chat-form__label">
|
<label class="chat-form__label">
|
||||||
<span>{{i18n "chat.settings.mobile_notification_level"}}</span>
|
<span>{{i18n "chat.settings.mobile_notification_level"}}</span>
|
||||||
<ChatChannelSettingsSavedIndicator
|
<ChatChannelSettingsSavedIndicator
|
||||||
@property={{this.channel.currentUserMembership.mobile_notification_level}}
|
@property={{this.channel.currentUserMembership.mobileNotificationLevel}}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div class="chat-form__control">
|
<div class="chat-form__control">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
@content={{this.notificationLevels}}
|
@content={{this.notificationLevels}}
|
||||||
@value={{this.channel.currentUserMembership.mobile_notification_level}}
|
@value={{this.channel.currentUserMembership.mobileNotificationLevel}}
|
||||||
@valueProperty="value"
|
@valueProperty="value"
|
||||||
@class="channel-settings-view__mobile-notification-level-selector"
|
@class="channel-settings-view__mobile-notification-level-selector"
|
||||||
@onChange={{action
|
@onChange={{fn
|
||||||
(fn this.saveNotificationSettings "mobile_notification_level")
|
this.saveNotificationSettings
|
||||||
|
"mobileNotificationLevel"
|
||||||
|
"mobile_notification_level"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -74,16 +78,16 @@
|
|||||||
<label class="chat-form__label">
|
<label class="chat-form__label">
|
||||||
<span>{{i18n "chat.settings.auto_join_users_label"}}</span>
|
<span>{{i18n "chat.settings.auto_join_users_label"}}</span>
|
||||||
<ChatChannelSettingsSavedIndicator
|
<ChatChannelSettingsSavedIndicator
|
||||||
@property={{this.channel.auto_join_users}}
|
@property={{this.channel.autoJoinUsers}}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<ComboBox
|
<ComboBox
|
||||||
@content={{this.autoAddUsersOptions}}
|
@content={{this.autoAddUsersOptions}}
|
||||||
@value={{this.channel.auto_join_users}}
|
@value={{this.channel.autoJoinUsers}}
|
||||||
@valueProperty="value"
|
@valueProperty="value"
|
||||||
@class="channel-settings-view__auto-join-selector"
|
@class="channel-settings-view__auto-join-selector"
|
||||||
@onChange={{action
|
@onChange={{action
|
||||||
(fn this.onToggleAutoJoinUsers this.channel.auto_join_users)
|
(fn this.onToggleAutoJoinUsers this.channel.autoJoinUsers)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<p class="chat-form__description -autojoin">
|
<p class="chat-form__description -autojoin">
|
||||||
@ -102,12 +106,12 @@
|
|||||||
<label class="chat-form__label">
|
<label class="chat-form__label">
|
||||||
<span>{{i18n "chat.settings.channel_wide_mentions_label"}}</span>
|
<span>{{i18n "chat.settings.channel_wide_mentions_label"}}</span>
|
||||||
<ChatChannelSettingsSavedIndicator
|
<ChatChannelSettingsSavedIndicator
|
||||||
@property={{this.channel.allow_channel_wide_mentions}}
|
@property={{this.channel.allowChannelWideMentions}}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<ComboBox
|
<ComboBox
|
||||||
@content={{this.channelWideMentionsOptions}}
|
@content={{this.channelWideMentionsOptions}}
|
||||||
@value={{this.channel.allow_channel_wide_mentions}}
|
@value={{this.channel.allowChannelWideMentions}}
|
||||||
@valueProperty="value"
|
@valueProperty="value"
|
||||||
@class="channel-settings-view__channel-wide-mentions-selector"
|
@class="channel-settings-view__channel-wide-mentions-selector"
|
||||||
@onChange={{this.onToggleChannelWideMentions}}
|
@onChange={{this.onToggleChannelWideMentions}}
|
||||||
|
@ -3,7 +3,6 @@ import { action, computed } from "@ember/object";
|
|||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import showModal from "discourse/lib/show-modal";
|
import showModal from "discourse/lib/show-modal";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { Promise } from "rsvp";
|
|
||||||
import { reads } from "@ember/object/computed";
|
import { reads } from "@ember/object/computed";
|
||||||
|
|
||||||
const NOTIFICATION_LEVELS = [
|
const NOTIFICATION_LEVELS = [
|
||||||
@ -79,29 +78,18 @@ export default class ChatChannelSettingsView extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
saveNotificationSettings(key, value) {
|
saveNotificationSettings(frontendKey, backendKey, newValue) {
|
||||||
if (this.channel[key] === value) {
|
if (this.channel.currentUserMembership[frontendKey] === newValue) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = {};
|
const settings = {};
|
||||||
settings[key] = value;
|
settings[backendKey] = newValue;
|
||||||
return this.chatApi
|
return this.chatApi
|
||||||
.updateCurrentUserChannelNotificationsSettings(this.channel.id, settings)
|
.updateCurrentUserChannelNotificationsSettings(this.channel.id, settings)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
[
|
this.channel.currentUserMembership[frontendKey] =
|
||||||
"muted",
|
result.membership[backendKey];
|
||||||
"desktop_notification_level",
|
|
||||||
"mobile_notification_level",
|
|
||||||
].forEach((property) => {
|
|
||||||
if (
|
|
||||||
result.membership[property] !==
|
|
||||||
this.channel.currentUserMembership[property]
|
|
||||||
) {
|
|
||||||
this.channel.currentUserMembership[property] =
|
|
||||||
result.membership[property];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +113,7 @@ export default class ChatChannelSettingsView extends Component {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
onToggleAutoJoinUsers() {
|
onToggleAutoJoinUsers() {
|
||||||
if (!this.channel.auto_join_users) {
|
if (!this.channel.autoJoinUsers) {
|
||||||
this.onEnableAutoJoinUsers();
|
this.onEnableAutoJoinUsers();
|
||||||
} else {
|
} else {
|
||||||
this.onDisableAutoJoinUsers();
|
this.onDisableAutoJoinUsers();
|
||||||
@ -134,40 +122,58 @@ export default class ChatChannelSettingsView extends Component {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
onToggleChannelWideMentions() {
|
onToggleChannelWideMentions() {
|
||||||
|
const newValue = !this.channel.allowChannelWideMentions;
|
||||||
|
if (this.channel.allowChannelWideMentions === newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return this._updateChannelProperty(
|
return this._updateChannelProperty(
|
||||||
this.channel,
|
this.channel,
|
||||||
"allow_channel_wide_mentions",
|
"allow_channel_wide_mentions",
|
||||||
!this.channel.allow_channel_wide_mentions
|
newValue
|
||||||
);
|
).then((result) => {
|
||||||
|
this.channel.allowChannelWideMentions =
|
||||||
|
result.channel.allow_channel_wide_mentions;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDisableAutoJoinUsers() {
|
onDisableAutoJoinUsers() {
|
||||||
return this._updateChannelProperty(this.channel, "auto_join_users", false);
|
if (this.channel.autoJoinUsers === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._updateChannelProperty(
|
||||||
|
this.channel,
|
||||||
|
"auto_join_users",
|
||||||
|
false
|
||||||
|
).then((result) => {
|
||||||
|
this.channel.autoJoinUsers = result.channel.auto_join_users;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnableAutoJoinUsers() {
|
onEnableAutoJoinUsers() {
|
||||||
|
if (this.channel.autoJoinUsers === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.dialog.confirm({
|
this.dialog.confirm({
|
||||||
message: I18n.t("chat.settings.auto_join_users_warning", {
|
message: I18n.t("chat.settings.auto_join_users_warning", {
|
||||||
category: this.channel.chatable.name,
|
category: this.channel.chatable.name,
|
||||||
}),
|
}),
|
||||||
didConfirm: () =>
|
didConfirm: () =>
|
||||||
this._updateChannelProperty(this.channel, "auto_join_users", true),
|
this._updateChannelProperty(this.channel, "auto_join_users", true).then(
|
||||||
|
(result) => {
|
||||||
|
this.channel.autoJoinUsers = result.channel.auto_join_users;
|
||||||
|
}
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateChannelProperty(channel, property, value) {
|
_updateChannelProperty(channel, property, value) {
|
||||||
if (channel[property] === value) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = {};
|
const payload = {};
|
||||||
payload[property] = value;
|
payload[property] = value;
|
||||||
return this.chatApi
|
|
||||||
.updateChannel(channel.id, payload)
|
return this.chatApi.updateChannel(channel.id, payload).catch((event) => {
|
||||||
.then((result) => {
|
|
||||||
channel.set(property, result.channel[property]);
|
|
||||||
})
|
|
||||||
.catch((event) => {
|
|
||||||
if (event.jqXHR?.responseJSON?.errors) {
|
if (event.jqXHR?.responseJSON?.errors) {
|
||||||
this.flash(event.jqXHR.responseJSON.errors.join("\n"), "error");
|
this.flash(event.jqXHR.responseJSON.errors.join("\n"), "error");
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
{{#if this.channel.isDraft}}
|
{{#if @channel.isDraft}}
|
||||||
<div class="chat-channel-title is-draft">
|
<div class="chat-channel-title is-draft">
|
||||||
<span class="chat-channel-title__name">{{this.channel.title}}</span>
|
<span class="chat-channel-title__name">{{@channel.title}}</span>
|
||||||
{{#if (has-block)}}
|
{{#if (has-block)}}
|
||||||
{{yield}}
|
{{yield}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if this.channel.isDirectMessageChannel}}
|
{{#if @channel.isDirectMessageChannel}}
|
||||||
<div class="chat-channel-title is-dm">
|
<div class="chat-channel-title is-dm">
|
||||||
|
|
||||||
<div class="chat-channel-title__avatar">
|
<div class="chat-channel-title__avatar">
|
||||||
{{#if this.multiDm}}
|
{{#if this.multiDm}}
|
||||||
<span class="chat-channel-title__users-count">
|
<span class="chat-channel-title__users-count">
|
||||||
{{this.channel.chatable.users.length}}
|
{{@channel.chatable.users.length}}
|
||||||
</span>
|
</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<ChatUserAvatar @user={{this.channel.chatable.users.firstObject}} />
|
<ChatUserAvatar @user={{@channel.chatable.users.firstObject}} />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -24,12 +24,12 @@
|
|||||||
{{#if this.multiDm}}
|
{{#if this.multiDm}}
|
||||||
<span class="chat-channel-title__name">{{this.usernames}}</span>
|
<span class="chat-channel-title__name">{{this.usernames}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#let this.channel.chatable.users.firstObject as |user|}}
|
{{#let @channel.chatable.users.firstObject as |user|}}
|
||||||
<span class="chat-channel-title__name">{{user.username}}</span>
|
<span class="chat-channel-title__name">{{user.username}}</span>
|
||||||
{{#if this.showUserStatus}}
|
{{#if this.showUserStatus}}
|
||||||
<UserStatusMessage
|
<UserStatusMessage
|
||||||
@class="chat-channel-title__user-status-message"
|
@class="chat-channel-title__user-status-message"
|
||||||
@status={{this.channel.chatable.users.firstObject.status}}
|
@status={{@channel.chatable.users.firstObject.status}}
|
||||||
@showDescription={{if this.site.mobileView "true"}}
|
@showDescription={{if this.site.mobileView "true"}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -48,19 +48,19 @@
|
|||||||
{{yield}}
|
{{yield}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{else if this.channel.isCategoryChannel}}
|
{{else if @channel.isCategoryChannel}}
|
||||||
<div class="chat-channel-title is-category">
|
<div class="chat-channel-title is-category">
|
||||||
<span
|
<span
|
||||||
class="chat-channel-title__category-badge"
|
class="chat-channel-title__category-badge"
|
||||||
style={{this.channelColorStyle}}
|
style={{this.channelColorStyle}}
|
||||||
>
|
>
|
||||||
{{d-icon "d-chat"}}
|
{{d-icon "d-chat"}}
|
||||||
{{#if this.channel.chatable.read_restricted}}
|
{{#if @channel.chatable.read_restricted}}
|
||||||
{{d-icon "lock" class="chat-channel-title__restricted-category-icon"}}
|
{{d-icon "lock" class="chat-channel-title__restricted-category-icon"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</span>
|
</span>
|
||||||
<span class="chat-channel-title__name">
|
<span class="chat-channel-title__name">
|
||||||
{{replace-emoji this.channel.title}}
|
{{replace-emoji @channel.title}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{{#if (has-block)}}
|
{{#if (has-block)}}
|
||||||
|
@ -1,33 +1,24 @@
|
|||||||
import Component from "@ember/component";
|
import Component from "@glimmer/component";
|
||||||
import { htmlSafe } from "@ember/template";
|
import { htmlSafe } from "@ember/template";
|
||||||
import { computed } from "@ember/object";
|
|
||||||
import { gt, reads } from "@ember/object/computed";
|
|
||||||
|
|
||||||
export default class ChatChannelTitle extends Component {
|
export default class ChatChannelTitle extends Component {
|
||||||
tagName = "";
|
get users() {
|
||||||
channel = null;
|
return this.args.channel.chatable.users;
|
||||||
|
}
|
||||||
|
|
||||||
@reads("channel.chatable.users.[]") users;
|
get multiDm() {
|
||||||
@gt("users.length", 1) multiDm;
|
return this.users.length > 1;
|
||||||
|
}
|
||||||
|
|
||||||
@computed("users")
|
|
||||||
get usernames() {
|
get usernames() {
|
||||||
return this.users.mapBy("username").join(", ");
|
return this.users.mapBy("username").join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed("channel.chatable.color")
|
|
||||||
get channelColorStyle() {
|
get channelColorStyle() {
|
||||||
return htmlSafe(`color: #${this.channel.chatable.color}`);
|
return htmlSafe(`color: #${this.args.channel.chatable.color}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed(
|
|
||||||
"channel.chatable.users.length",
|
|
||||||
"channel.chatable.users.@each.status"
|
|
||||||
)
|
|
||||||
get showUserStatus() {
|
get showUserStatus() {
|
||||||
return !!(
|
return !!(this.users.length === 1 && this.users[0].status);
|
||||||
this.channel.chatable.users.length === 1 &&
|
|
||||||
this.channel.chatable.users[0].status
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
{{#if (gt @channel.currentUserMembership.unread_count 0)}}
|
{{#if (gt @channel.currentUserMembership.unreadCount 0)}}
|
||||||
<div
|
<div
|
||||||
class={{concat-class
|
class={{concat-class
|
||||||
"chat-channel-unread-indicator"
|
"chat-channel-unread-indicator"
|
||||||
(if
|
(if
|
||||||
(or
|
(or
|
||||||
@channel.isDirectMessageChannel
|
@channel.isDirectMessageChannel
|
||||||
(gt @channel.currentUserMembership.unread_mentions 0)
|
(gt @channel.currentUserMembership.unreadMentions 0)
|
||||||
)
|
)
|
||||||
"urgent"
|
"urgent"
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="number">{{@channel.currentUserMembership.unread_count}}</div>
|
<div class="number">{{@channel.currentUserMembership.unreadCount}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
@ -165,7 +165,7 @@ export default class ChatLivePane extends Component {
|
|||||||
findArgs["targetMessageId"] = this.requestedTargetMessageId;
|
findArgs["targetMessageId"] = this.requestedTargetMessageId;
|
||||||
} else if (fetchingFromLastRead) {
|
} else if (fetchingFromLastRead) {
|
||||||
findArgs["targetMessageId"] =
|
findArgs["targetMessageId"] =
|
||||||
this.args.channel.currentUserMembership.last_read_message_id;
|
this.args.channel.currentUserMembership.lastReadMessageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.chatApi
|
return this.chatApi
|
||||||
@ -346,7 +346,7 @@ export default class ChatLivePane extends Component {
|
|||||||
if (
|
if (
|
||||||
!foundFirstNew &&
|
!foundFirstNew &&
|
||||||
messageData.id >
|
messageData.id >
|
||||||
this.args.channel.currentUserMembership.last_read_message_id &&
|
this.args.channel.currentUserMembership.lastReadMessageId &&
|
||||||
!channel.messages.some((m) => m.newest)
|
!channel.messages.some((m) => m.newest)
|
||||||
) {
|
) {
|
||||||
foundFirstNew = true;
|
foundFirstNew = true;
|
||||||
@ -444,7 +444,7 @@ export default class ChatLivePane extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lastReadId =
|
const lastReadId =
|
||||||
this.args.channel.currentUserMembership?.last_read_message_id;
|
this.args.channel.currentUserMembership?.lastReadMessageId;
|
||||||
let lastUnreadVisibleMessage = this.args.channel.visibleMessages.findLast(
|
let lastUnreadVisibleMessage = this.args.channel.visibleMessages.findLast(
|
||||||
(message) => !lastReadId || message.id > lastReadId
|
(message) => !lastReadId || message.id > lastReadId
|
||||||
);
|
);
|
||||||
|
@ -93,7 +93,7 @@
|
|||||||
|
|
||||||
{{#if this.shouldRenderReplyingIndicator}}
|
{{#if this.shouldRenderReplyingIndicator}}
|
||||||
<div class="chat-replying-indicator-container">
|
<div class="chat-replying-indicator-container">
|
||||||
<ChatReplyingIndicator @chatChannel={{@channel}} />
|
<ChatReplyingIndicator @channel={{@channel}} />
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { inject as service } from "@ember/service";
|
|||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { cloneJSON } from "discourse-common/lib/object";
|
import { cloneJSON } from "discourse-common/lib/object";
|
||||||
|
|
||||||
export default class ChatDraftChannelScreen extends Component {
|
export default class ChatDraftChannelScreen extends Component {
|
||||||
@service chat;
|
@service chat;
|
||||||
@service router;
|
@service router;
|
||||||
@ -20,7 +21,7 @@ export default class ChatDraftChannelScreen extends Component {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
onSwitchFromDraftChannel(channel) {
|
onSwitchFromDraftChannel(channel) {
|
||||||
channel.set("isDraft", false);
|
channel.isDraft = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_fetchPreviewedChannel(users) {
|
_fetchPreviewedChannel(users) {
|
||||||
@ -29,20 +30,16 @@ export default class ChatDraftChannelScreen extends Component {
|
|||||||
return this.chat
|
return this.chat
|
||||||
.getDmChannelForUsernames(users.mapBy("username"))
|
.getDmChannelForUsernames(users.mapBy("username"))
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.set(
|
const channel = ChatChannel.create(response.channel);
|
||||||
"previewedChannel",
|
channel.isDraft = true;
|
||||||
ChatChannel.create(
|
this.set("previewedChannel", channel);
|
||||||
Object.assign({}, response.channel, { isDraft: true })
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
if (error?.jqXHR?.status === 404) {
|
if (error?.jqXHR?.status === 404) {
|
||||||
this.set(
|
this.set(
|
||||||
"previewedChannel",
|
"previewedChannel",
|
||||||
ChatChannel.create({
|
ChatChannel.createDirectMessageChannelDraft({
|
||||||
chatable: { users: cloneJSON(users) },
|
users: cloneJSON(users),
|
||||||
isDraft: true,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,15 @@
|
|||||||
<div class="chat-replying-indicator">
|
{{#if @channel}}
|
||||||
{{#if this.shouldDisplay}}
|
<div
|
||||||
|
class={{concat-class
|
||||||
|
"chat-replying-indicator"
|
||||||
|
(if this.presenceChannel.subscribed "is-subscribed")
|
||||||
|
}}
|
||||||
|
{{did-insert this.subscribe}}
|
||||||
|
{{did-update this.handleDraft @channel.isDraft}}
|
||||||
|
{{did-update this.subscribe this.channelName}}
|
||||||
|
{{will-destroy this.teardown}}
|
||||||
|
>
|
||||||
|
{{#if this.shouldRender}}
|
||||||
<span class="chat-replying-indicator__text">{{this.text}}</span>
|
<span class="chat-replying-indicator__text">{{this.text}}</span>
|
||||||
<span class="chat-replying-indicator__wave">
|
<span class="chat-replying-indicator__wave">
|
||||||
<span class="chat-replying-indicator__dot">.</span>
|
<span class="chat-replying-indicator__dot">.</span>
|
||||||
@ -8,3 +18,4 @@
|
|||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
@ -1,89 +1,93 @@
|
|||||||
import { isBlank, isPresent } from "@ember/utils";
|
import { isBlank, isPresent } from "@ember/utils";
|
||||||
import Component from "@ember/component";
|
import Component from "@glimmer/component";
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { fmt } from "discourse/lib/computed";
|
import { action } from "@ember/object";
|
||||||
import { next } from "@ember/runloop";
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
|
||||||
export default Component.extend({
|
export default class ChatReplyingIndicator extends Component {
|
||||||
tagName: "",
|
@service currentUser;
|
||||||
presence: service(),
|
@service presence;
|
||||||
presenceChannel: null,
|
|
||||||
chatChannel: null,
|
|
||||||
|
|
||||||
@discourseComputed("presenceChannel.users.[]")
|
@tracked presenceChannel = null;
|
||||||
usernames(users) {
|
@tracked users = [];
|
||||||
return users
|
|
||||||
?.filter((u) => u.id !== this.currentUser.id)
|
|
||||||
?.mapBy("username");
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("usernames.[]")
|
@action
|
||||||
text(usernames) {
|
async subscribe() {
|
||||||
if (isBlank(usernames)) {
|
this.presenceChannel = this.presence.getChannel(this.channelName);
|
||||||
|
this.presenceChannel.on("change", this.handlePresenceChange);
|
||||||
|
await this.presenceChannel.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async resubscribe() {
|
||||||
|
await this.teardown();
|
||||||
|
await this.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
handlePresenceChange(presenceChannel) {
|
||||||
|
this.users = presenceChannel.users || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async handleDraft() {
|
||||||
|
if (this.args.channel.isDraft) {
|
||||||
|
await this.teardown();
|
||||||
|
} else {
|
||||||
|
await this.resubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async teardown() {
|
||||||
|
if (this.presenceChannel) {
|
||||||
|
await this.presenceChannel.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get usernames() {
|
||||||
|
return this.users
|
||||||
|
.filter((u) => u.id !== this.currentUser.id)
|
||||||
|
.mapBy("username");
|
||||||
|
}
|
||||||
|
|
||||||
|
get text() {
|
||||||
|
if (isBlank(this.usernames)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usernames.length === 1) {
|
if (this.usernames.length === 1) {
|
||||||
return I18n.t("chat.replying_indicator.single_user", {
|
return I18n.t("chat.replying_indicator.single_user", {
|
||||||
username: usernames[0],
|
username: this.usernames[0],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usernames.length < 4) {
|
if (this.usernames.length < 4) {
|
||||||
const lastUsername = usernames.pop();
|
const lastUsername = this.usernames[this.usernames.length - 1];
|
||||||
const commaSeparatedUsernames = usernames.join(
|
const commaSeparatedUsernames = this.usernames
|
||||||
I18n.t("word_connector.comma")
|
.slice(0, this.usernames.length - 1)
|
||||||
);
|
.join(I18n.t("word_connector.comma"));
|
||||||
return I18n.t("chat.replying_indicator.multiple_users", {
|
return I18n.t("chat.replying_indicator.multiple_users", {
|
||||||
commaSeparatedUsernames,
|
commaSeparatedUsernames,
|
||||||
lastUsername,
|
lastUsername,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const commaSeparatedUsernames = usernames
|
const commaSeparatedUsernames = this.usernames
|
||||||
.slice(0, 2)
|
.slice(0, 2)
|
||||||
.join(I18n.t("word_connector.comma"));
|
.join(I18n.t("word_connector.comma"));
|
||||||
return I18n.t("chat.replying_indicator.many_users", {
|
return I18n.t("chat.replying_indicator.many_users", {
|
||||||
commaSeparatedUsernames,
|
commaSeparatedUsernames,
|
||||||
count: usernames.length - 2,
|
count: this.usernames.length - 2,
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("usernames.[]")
|
|
||||||
shouldDisplay(usernames) {
|
|
||||||
return isPresent(usernames);
|
|
||||||
},
|
|
||||||
|
|
||||||
channelName: fmt("chatChannel.id", "/chat-reply/%@"),
|
|
||||||
|
|
||||||
didReceiveAttrs() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
if (!this.chatChannel || this.chatChannel.isDraft) {
|
|
||||||
this.presenceChannel?.unsubscribe();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.presenceChannel?.name !== this.channelName) {
|
|
||||||
this.presenceChannel?.unsubscribe();
|
|
||||||
|
|
||||||
next(() => {
|
|
||||||
if (this.isDestroyed || this.isDestroying) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const presenceChannel = this.presence.getChannel(this.channelName);
|
|
||||||
this.set("presenceChannel", presenceChannel);
|
|
||||||
presenceChannel.subscribe();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
willDestroyElement() {
|
get shouldRender() {
|
||||||
this._super(...arguments);
|
return isPresent(this.usernames);
|
||||||
|
}
|
||||||
|
|
||||||
this.presenceChannel?.unsubscribe();
|
get channelName() {
|
||||||
},
|
return `/chat-reply/${this.args.channel.id}`;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
@ -75,14 +75,14 @@ export default class ChatSelectionManager extends Component {
|
|||||||
const openOpts = {};
|
const openOpts = {};
|
||||||
|
|
||||||
if (this.chatChannel.isCategoryChannel) {
|
if (this.chatChannel.isCategoryChannel) {
|
||||||
openOpts.categoryId = this.chatChannel.chatable_id;
|
openOpts.categoryId = this.chatChannel.chatableId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.site.mobileView) {
|
if (this.site.mobileView) {
|
||||||
// go to the relevant chatable (e.g. category) and open the
|
// go to the relevant chatable (e.g. category) and open the
|
||||||
// composer to insert text
|
// composer to insert text
|
||||||
if (this.chatChannel.chatable_url) {
|
if (this.chatChannel.chatableUrl) {
|
||||||
this.router.transitionTo(this.chatChannel.chatable_url);
|
this.router.transitionTo(this.chatChannel.chatableUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
await composer.focusComposer({
|
await composer.focusComposer({
|
||||||
|
@ -8,7 +8,7 @@ import { INPUT_DELAY } from "discourse-common/config/environment";
|
|||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import { schedule } from "@ember/runloop";
|
import { schedule } from "@ember/runloop";
|
||||||
import { gt, not } from "@ember/object/computed";
|
import { gt, not } from "@ember/object/computed";
|
||||||
import { createDirectMessageChannelDraft } from "discourse/plugins/chat/discourse/models/chat-channel";
|
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "",
|
tagName: "",
|
||||||
@ -29,7 +29,7 @@ export default Component.extend({
|
|||||||
|
|
||||||
this.set("users", []);
|
this.set("users", []);
|
||||||
this.set("selectedUsers", []);
|
this.set("selectedUsers", []);
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
|
@ -154,7 +154,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const highlightable = [`@${this.currentUser.username}`];
|
const highlightable = [`@${this.currentUser.username}`];
|
||||||
if (chatChannel.allow_channel_wide_mentions) {
|
if (chatChannel.allowChannelWideMentions) {
|
||||||
highlightable.push(...MENTION_KEYWORDS.map((k) => `@${k}`));
|
highlightable.push(...MENTION_KEYWORDS.map((k) => `@${k}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,13 +88,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get suffixValue() {
|
get suffixValue() {
|
||||||
return this.channel.currentUserMembership.unread_count > 0
|
return this.channel.currentUserMembership.unreadCount > 0
|
||||||
? "circle"
|
? "circle"
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
get suffixCSSClass() {
|
get suffixCSSClass() {
|
||||||
return this.channel.currentUserMembership.unread_mentions > 0
|
return this.channel.currentUserMembership.unreadMentions > 0
|
||||||
? "urgent"
|
? "urgent"
|
||||||
: "unread";
|
: "unread";
|
||||||
}
|
}
|
||||||
@ -282,7 +282,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get suffixValue() {
|
get suffixValue() {
|
||||||
return this.channel.currentUserMembership.unread_count > 0
|
return this.channel.currentUserMembership.unreadCount > 0
|
||||||
? "circle"
|
? "circle"
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
|
||||||
|
export default class ChatChannelArchive {
|
||||||
|
static create(args = {}) {
|
||||||
|
return new ChatChannelArchive(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@tracked failed;
|
||||||
|
@tracked completed;
|
||||||
|
@tracked messages;
|
||||||
|
@tracked topicId;
|
||||||
|
@tracked totalMessages;
|
||||||
|
|
||||||
|
constructor(args = {}) {
|
||||||
|
this.failed = args.archive_failed;
|
||||||
|
this.completed = args.archive_completed;
|
||||||
|
this.messages = args.archived_messages;
|
||||||
|
this.topicId = args.archive_topic_id;
|
||||||
|
this.totalMessages = args.total_messages;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
import RestModel from "discourse/models/rest";
|
|
||||||
import User from "discourse/models/user";
|
|
||||||
import UserChatChannelMembership from "discourse/plugins/chat/discourse/models/user-chat-channel-membership";
|
import UserChatChannelMembership from "discourse/plugins/chat/discourse/models/user-chat-channel-membership";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { escapeExpression } from "discourse/lib/utilities";
|
import { escapeExpression } from "discourse/lib/utilities";
|
||||||
@ -10,6 +8,9 @@ import ChatMessagesManager from "discourse/plugins/chat/discourse/lib/chat-messa
|
|||||||
import { getOwner } from "discourse-common/lib/get-owner";
|
import { getOwner } from "discourse-common/lib/get-owner";
|
||||||
import guid from "pretty-text/guid";
|
import guid from "pretty-text/guid";
|
||||||
import ChatThread from "discourse/plugins/chat/discourse/models/chat-thread";
|
import ChatThread from "discourse/plugins/chat/discourse/models/chat-thread";
|
||||||
|
import ChatDirectMessage from "discourse/plugins/chat/discourse/models/chat-direct-message";
|
||||||
|
import ChatChannelArchive from "discourse/plugins/chat/discourse/models/chat-channel-archive";
|
||||||
|
import Category from "discourse/models/category";
|
||||||
|
|
||||||
export const CHATABLE_TYPES = {
|
export const CHATABLE_TYPES = {
|
||||||
directMessageChannel: "DirectMessage",
|
directMessageChannel: "DirectMessage",
|
||||||
@ -49,25 +50,79 @@ const READONLY_STATUSES = [
|
|||||||
CHANNEL_STATUSES.archived,
|
CHANNEL_STATUSES.archived,
|
||||||
];
|
];
|
||||||
|
|
||||||
export default class ChatChannel extends RestModel {
|
export default class ChatChannel {
|
||||||
|
static create(args = {}) {
|
||||||
|
return new ChatChannel(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createDirectMessageChannelDraft(args = {}) {
|
||||||
|
const channel = ChatChannel.create({
|
||||||
|
chatable_type: CHATABLE_TYPES.directMessageChannel,
|
||||||
|
chatable: {
|
||||||
|
users: args.users || [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
channel.isDraft = true;
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
@tracked currentUserMembership = null;
|
@tracked currentUserMembership = null;
|
||||||
@tracked isDraft = false;
|
@tracked isDraft = false;
|
||||||
@tracked title;
|
@tracked title;
|
||||||
|
@tracked slug;
|
||||||
@tracked description;
|
@tracked description;
|
||||||
@tracked chatableType;
|
|
||||||
@tracked status;
|
@tracked status;
|
||||||
@tracked activeThread;
|
@tracked activeThread = null;
|
||||||
@tracked lastMessageSentAt;
|
@tracked lastMessageSentAt;
|
||||||
@tracked canDeleteOthers;
|
@tracked canDeleteOthers;
|
||||||
@tracked canDeleteSelf;
|
@tracked canDeleteSelf;
|
||||||
@tracked canFlag;
|
@tracked canFlag;
|
||||||
@tracked canModerate;
|
@tracked canModerate;
|
||||||
@tracked userSilenced;
|
@tracked userSilenced;
|
||||||
@tracked draft;
|
@tracked draft = null;
|
||||||
|
@tracked meta;
|
||||||
|
@tracked chatableType;
|
||||||
|
@tracked chatableUrl;
|
||||||
|
@tracked autoJoinUsers = false;
|
||||||
|
@tracked allowChannelWideMentions = true;
|
||||||
|
@tracked membershipsCount = 0;
|
||||||
|
@tracked archive;
|
||||||
|
|
||||||
threadsManager = new ChatThreadsManager(getOwner(this));
|
threadsManager = new ChatThreadsManager(getOwner(this));
|
||||||
messagesManager = new ChatMessagesManager(getOwner(this));
|
messagesManager = new ChatMessagesManager(getOwner(this));
|
||||||
|
|
||||||
|
constructor(args = {}) {
|
||||||
|
this.id = args.id;
|
||||||
|
this.chatableId = args.chatable_id;
|
||||||
|
this.chatableUrl = args.chatable_url;
|
||||||
|
this.chatableType = args.chatable_type;
|
||||||
|
this.membershipsCount = args.memberships_count;
|
||||||
|
this.meta = args.meta;
|
||||||
|
this.slug = args.slug;
|
||||||
|
this.title = args.title;
|
||||||
|
this.status = args.status;
|
||||||
|
this.canDeleteSelf = args.can_delete_self;
|
||||||
|
this.canDeleteOthers = args.can_delete_others;
|
||||||
|
this.canFlag = args.can_flag;
|
||||||
|
this.userSilenced = args.user_silenced;
|
||||||
|
this.canModerate = args.can_moderate;
|
||||||
|
this.description = args.description;
|
||||||
|
this.lastMessageSentAt = args.last_message_sent_at;
|
||||||
|
this.threadingEnabled = args.threading_enabled;
|
||||||
|
this.autoJoinUsers = args.auto_join_users;
|
||||||
|
this.allowChannelWideMentions = args.allow_channel_wide_mentions;
|
||||||
|
this.chatable = this.isDirectMessageChannel
|
||||||
|
? ChatDirectMessage.create(args)
|
||||||
|
: Category.create(args.chatable);
|
||||||
|
this.currentUserMembership = UserChatChannelMembership.create(
|
||||||
|
args.current_user_membership
|
||||||
|
);
|
||||||
|
|
||||||
|
if (args.archive_completed || args.archive_failed) {
|
||||||
|
this.archive = ChatChannelArchive.create(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
findIndexOfMessage(id) {
|
findIndexOfMessage(id) {
|
||||||
return this.messagesManager.findIndexOfMessage(id);
|
return this.messagesManager.findIndexOfMessage(id);
|
||||||
}
|
}
|
||||||
@ -223,14 +278,14 @@ export default class ChatChannel extends RestModel {
|
|||||||
|
|
||||||
updateMembership(membership) {
|
updateMembership(membership) {
|
||||||
this.currentUserMembership.following = membership.following;
|
this.currentUserMembership.following = membership.following;
|
||||||
this.currentUserMembership.last_read_message_id =
|
this.currentUserMembership.lastReadMessage_id =
|
||||||
membership.last_read_message_id;
|
membership.last_read_message_id;
|
||||||
this.currentUserMembership.desktop_notification_level =
|
this.currentUserMembership.desktopNotificationLevel =
|
||||||
membership.desktop_notification_level;
|
membership.desktop_notification_level;
|
||||||
this.currentUserMembership.mobile_notification_level =
|
this.currentUserMembership.mobileNotificationLevel =
|
||||||
membership.mobile_notification_level;
|
membership.mobile_notification_level;
|
||||||
this.currentUserMembership.unread_count = membership.unread_count;
|
this.currentUserMembership.unreadCount = membership.unread_count;
|
||||||
this.currentUserMembership.unread_mentions = membership.unread_mentions;
|
this.currentUserMembership.unreadMentions = membership.unread_mentions;
|
||||||
this.currentUserMembership.muted = membership.muted;
|
this.currentUserMembership.muted = membership.muted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +294,7 @@ export default class ChatChannel extends RestModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.currentUserMembership.last_read_message_id >= messageId) {
|
if (this.currentUserMembership.lastReadMessageId >= messageId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,59 +305,3 @@ export default class ChatChannel extends RestModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatChannel.reopenClass({
|
|
||||||
create(args) {
|
|
||||||
args = args || {};
|
|
||||||
|
|
||||||
this._initUserModels(args);
|
|
||||||
this._initUserMembership(args);
|
|
||||||
|
|
||||||
this._remapKey(args, "chatable_type", "chatableType");
|
|
||||||
this._remapKey(args, "memberships_count", "membershipsCount");
|
|
||||||
this._remapKey(args, "last_message_sent_at", "lastMessageSentAt");
|
|
||||||
this._remapKey(args, "threading_enabled", "threadingEnabled");
|
|
||||||
|
|
||||||
return this._super(args);
|
|
||||||
},
|
|
||||||
|
|
||||||
_remapKey(obj, oldKey, newKey) {
|
|
||||||
delete Object.assign(obj, { [newKey]: obj[oldKey] })[oldKey];
|
|
||||||
},
|
|
||||||
|
|
||||||
_initUserModels(args) {
|
|
||||||
if (args.chatable?.users?.length) {
|
|
||||||
for (let i = 0; i < args.chatable?.users?.length; i++) {
|
|
||||||
const userData = args.chatable.users[i];
|
|
||||||
args.chatable.users[i] = User.create(userData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_initUserMembership(args) {
|
|
||||||
if (args.currentUserMembership instanceof UserChatChannelMembership) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.currentUserMembership = UserChatChannelMembership.create(
|
|
||||||
args.current_user_membership || {
|
|
||||||
following: false,
|
|
||||||
muted: false,
|
|
||||||
unread_count: 0,
|
|
||||||
unread_mentions: 0,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
delete args.current_user_membership;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export function createDirectMessageChannelDraft() {
|
|
||||||
return ChatChannel.create({
|
|
||||||
isDraft: true,
|
|
||||||
chatable_type: CHATABLE_TYPES.directMessageChannel,
|
|
||||||
chatable: {
|
|
||||||
users: [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
import User from "discourse/models/user";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
|
||||||
|
export default class ChatDirectMessage {
|
||||||
|
static create(args = {}) {
|
||||||
|
return new ChatDirectMessage(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@tracked id;
|
||||||
|
@tracked users = null;
|
||||||
|
|
||||||
|
constructor(args = {}) {
|
||||||
|
this.id = args.chatable.id;
|
||||||
|
this.users = this.#initUsers(args.chatable.users || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
#initUsers(users) {
|
||||||
|
return users.map((user) => {
|
||||||
|
if (!user || user instanceof User) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return User.create(user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -166,7 +166,7 @@ export default class ChatMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get read() {
|
get read() {
|
||||||
return this.channel.currentUserMembership?.last_read_message_id >= this.id;
|
return this.channel.currentUserMembership?.lastReadMessageId >= this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
get firstMessageOfTheDayAt() {
|
get firstMessageOfTheDayAt() {
|
||||||
|
@ -1,28 +1,36 @@
|
|||||||
import RestModel from "discourse/models/rest";
|
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { tracked } from "@glimmer/tracking";
|
||||||
import User from "discourse/models/user";
|
import User from "discourse/models/user";
|
||||||
export default class UserChatChannelMembership extends RestModel {
|
|
||||||
|
export default class UserChatChannelMembership {
|
||||||
|
static create(args = {}) {
|
||||||
|
return new UserChatChannelMembership(args);
|
||||||
|
}
|
||||||
|
|
||||||
@tracked following = false;
|
@tracked following = false;
|
||||||
@tracked muted = false;
|
@tracked muted = false;
|
||||||
@tracked unread_count = 0;
|
@tracked unreadCount = 0;
|
||||||
@tracked unread_mentions = 0;
|
@tracked unreadMentions = 0;
|
||||||
@tracked desktop_notification_level = null;
|
@tracked desktopNotificationLevel = null;
|
||||||
@tracked mobile_notification_level = null;
|
@tracked mobileNotificationLevel = null;
|
||||||
@tracked last_read_message_id = null;
|
@tracked lastReadMessageId = null;
|
||||||
|
@tracked user = null;
|
||||||
|
|
||||||
|
constructor(args = {}) {
|
||||||
|
this.following = args.following;
|
||||||
|
this.muted = args.muted;
|
||||||
|
this.unreadCount = args.unread_count;
|
||||||
|
this.unreadMentions = args.unread_mentions;
|
||||||
|
this.desktopNotificationLevel = args.desktop_notification_level;
|
||||||
|
this.mobileNotificationLevel = args.mobile_notification_level;
|
||||||
|
this.lastReadMessageId = args.last_read_message_id;
|
||||||
|
this.user = this.#initUserModel(args.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserChatChannelMembership.reopenClass({
|
#initUserModel(user) {
|
||||||
create(args) {
|
if (!user || user instanceof User) {
|
||||||
args = args || {};
|
return user;
|
||||||
this._initUser(args);
|
|
||||||
return this._super(args);
|
|
||||||
},
|
|
||||||
|
|
||||||
_initUser(args) {
|
|
||||||
if (!args.user || args.user instanceof User) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
args.user = User.create(args.user);
|
return User.create(user);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
@ -61,11 +61,10 @@ export default class ChatChannelsManager extends Service {
|
|||||||
return this.chatApi.followChannel(model.id).then((membership) => {
|
return this.chatApi.followChannel(model.id).then((membership) => {
|
||||||
model.currentUserMembership.following = membership.following;
|
model.currentUserMembership.following = membership.following;
|
||||||
model.currentUserMembership.muted = membership.muted;
|
model.currentUserMembership.muted = membership.muted;
|
||||||
model.currentUserMembership.desktop_notification_level =
|
model.currentUserMembership.desktopNotificationLevel =
|
||||||
membership.desktop_notification_level;
|
membership.desktopNotificationLevel;
|
||||||
model.currentUserMembership.mobile_notification_level =
|
model.currentUserMembership.mobileNotificationLevel =
|
||||||
membership.mobile_notification_level;
|
membership.mobileNotificationLevel;
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -97,7 +96,7 @@ export default class ChatChannelsManager extends Service {
|
|||||||
get unreadCount() {
|
get unreadCount() {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
this.publicMessageChannels.forEach((channel) => {
|
this.publicMessageChannels.forEach((channel) => {
|
||||||
count += channel.currentUserMembership.unread_count || 0;
|
count += channel.currentUserMembership.unreadCount || 0;
|
||||||
});
|
});
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@ -106,9 +105,9 @@ export default class ChatChannelsManager extends Service {
|
|||||||
let count = 0;
|
let count = 0;
|
||||||
this.channels.forEach((channel) => {
|
this.channels.forEach((channel) => {
|
||||||
if (channel.isDirectMessageChannel) {
|
if (channel.isDirectMessageChannel) {
|
||||||
count += channel.currentUserMembership.unread_count || 0;
|
count += channel.currentUserMembership.unreadCount || 0;
|
||||||
}
|
}
|
||||||
count += channel.currentUserMembership.unread_mentions || 0;
|
count += channel.currentUserMembership.unreadMentions || 0;
|
||||||
});
|
});
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@ -159,8 +158,8 @@ export default class ChatChannelsManager extends Service {
|
|||||||
|
|
||||||
#sortDirectMessageChannels(channels) {
|
#sortDirectMessageChannels(channels) {
|
||||||
return channels.sort((a, b) => {
|
return channels.sort((a, b) => {
|
||||||
const unreadCountA = a.currentUserMembership.unread_count || 0;
|
const unreadCountA = a.currentUserMembership.unreadCount || 0;
|
||||||
const unreadCountB = b.currentUserMembership.unread_count || 0;
|
const unreadCountB = b.currentUserMembership.unreadCount || 0;
|
||||||
if (unreadCountA === unreadCountB) {
|
if (unreadCountA === unreadCountB) {
|
||||||
return new Date(a.lastMessageSentAt) > new Date(b.lastMessageSentAt)
|
return new Date(a.lastMessageSentAt) > new Date(b.lastMessageSentAt)
|
||||||
? -1
|
? -1
|
||||||
|
@ -2,6 +2,7 @@ import Service, { inject as service } from "@ember/service";
|
|||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
import { CHANNEL_STATUSES } from "discourse/plugins/chat/discourse/models/chat-channel";
|
import { CHANNEL_STATUSES } from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||||
|
import ChatChannelArchive from "../models/chat-channel-archive";
|
||||||
|
|
||||||
export default class ChatSubscriptionsManager extends Service {
|
export default class ChatSubscriptionsManager extends Service {
|
||||||
@service store;
|
@service store;
|
||||||
@ -125,13 +126,7 @@ export default class ChatSubscriptionsManager extends Service {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.setProperties({
|
channel.archive = ChatChannelArchive.create(busData);
|
||||||
archive_failed: busData.archive_failed,
|
|
||||||
archive_completed: busData.archive_completed,
|
|
||||||
archived_messages: busData.archived_messages,
|
|
||||||
archive_topic_id: busData.archive_topic_id,
|
|
||||||
total_messages: busData.total_messages,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,8 +134,8 @@ export default class ChatSubscriptionsManager extends Service {
|
|||||||
_onNewMentions(busData) {
|
_onNewMentions(busData) {
|
||||||
this.chatChannelsManager.find(busData.channel_id).then((channel) => {
|
this.chatChannelsManager.find(busData.channel_id).then((channel) => {
|
||||||
const membership = channel.currentUserMembership;
|
const membership = channel.currentUserMembership;
|
||||||
if (busData.message_id > membership?.last_read_message_id) {
|
if (busData.message_id > membership?.lastReadMessageId) {
|
||||||
membership.unread_mentions = (membership.unread_mentions || 0) + 1;
|
membership.unreadMentions = (membership.unreadMentions || 0) + 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -186,20 +181,18 @@ export default class ChatSubscriptionsManager extends Service {
|
|||||||
this.chatChannelsManager.find(busData.channel_id).then((channel) => {
|
this.chatChannelsManager.find(busData.channel_id).then((channel) => {
|
||||||
if (busData.user_id === this.currentUser.id) {
|
if (busData.user_id === this.currentUser.id) {
|
||||||
// User sent message, update tracking state to no unread
|
// User sent message, update tracking state to no unread
|
||||||
channel.currentUserMembership.last_read_message_id = busData.message_id;
|
channel.currentUserMembership.lastReadMessageId = busData.message_id;
|
||||||
} else {
|
} else {
|
||||||
// Ignored user sent message, update tracking state to no unread
|
// Ignored user sent message, update tracking state to no unread
|
||||||
if (this.currentUser.ignored_users.includes(busData.username)) {
|
if (this.currentUser.ignored_users.includes(busData.username)) {
|
||||||
channel.currentUserMembership.last_read_message_id =
|
channel.currentUserMembership.lastReadMessageId = busData.message_id;
|
||||||
busData.message_id;
|
|
||||||
} else {
|
} else {
|
||||||
// Message from other user. Increment trackings state
|
// Message from other user. Increment trackings state
|
||||||
if (
|
if (
|
||||||
busData.message_id >
|
busData.message_id >
|
||||||
(channel.currentUserMembership.last_read_message_id || 0)
|
(channel.currentUserMembership.lastReadMessageId || 0)
|
||||||
) {
|
) {
|
||||||
channel.currentUserMembership.unread_count =
|
channel.currentUserMembership.unreadCount++;
|
||||||
channel.currentUserMembership.unread_count + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,11 +249,10 @@ export default class ChatSubscriptionsManager extends Service {
|
|||||||
@bind
|
@bind
|
||||||
_updateChannelTrackingData(channelId, trackingData) {
|
_updateChannelTrackingData(channelId, trackingData) {
|
||||||
this.chatChannelsManager.find(channelId).then((channel) => {
|
this.chatChannelsManager.find(channelId).then((channel) => {
|
||||||
channel.currentUserMembership.last_read_message_id =
|
channel.currentUserMembership.lastReadMessageId =
|
||||||
trackingData.last_read_message_id;
|
trackingData.last_read_message_id;
|
||||||
channel.currentUserMembership.unread_count = trackingData.unread_count;
|
channel.currentUserMembership.unreadCount = trackingData.unread_count;
|
||||||
channel.currentUserMembership.unread_mentions =
|
channel.currentUserMembership.unreadMentions = trackingData.mention_count;
|
||||||
trackingData.mention_count;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +281,7 @@ export default class ChatSubscriptionsManager extends Service {
|
|||||||
channel.isDirectMessageChannel &&
|
channel.isDirectMessageChannel &&
|
||||||
!channel.currentUserMembership.following
|
!channel.currentUserMembership.following
|
||||||
) {
|
) {
|
||||||
channel.currentUserMembership.unread_count = 1;
|
channel.currentUserMembership.unreadCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chatChannelsManager.follow(channel);
|
this.chatChannelsManager.follow(channel);
|
||||||
@ -341,9 +333,7 @@ export default class ChatSubscriptionsManager extends Service {
|
|||||||
.find(busData.chat_channel_id, { fetchIfNotFound: false })
|
.find(busData.chat_channel_id, { fetchIfNotFound: false })
|
||||||
.then((channel) => {
|
.then((channel) => {
|
||||||
if (channel) {
|
if (channel) {
|
||||||
channel.setProperties({
|
channel.membershipsCount = busData.memberships_count;
|
||||||
memberships_count: busData.memberships_count,
|
|
||||||
});
|
|
||||||
this.appEvents.trigger("chat:refresh-channel-members");
|
this.appEvents.trigger("chat:refresh-channel-members");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -353,11 +343,9 @@ export default class ChatSubscriptionsManager extends Service {
|
|||||||
_onChannelEdits(busData) {
|
_onChannelEdits(busData) {
|
||||||
this.chatChannelsManager.find(busData.chat_channel_id).then((channel) => {
|
this.chatChannelsManager.find(busData.chat_channel_id).then((channel) => {
|
||||||
if (channel) {
|
if (channel) {
|
||||||
channel.setProperties({
|
channel.title = busData.name;
|
||||||
title: busData.name,
|
channel.description = busData.description;
|
||||||
description: busData.description,
|
channel.slug = busData.slug;
|
||||||
slug: busData.slug,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -365,15 +353,15 @@ export default class ChatSubscriptionsManager extends Service {
|
|||||||
@bind
|
@bind
|
||||||
_onChannelStatus(busData) {
|
_onChannelStatus(busData) {
|
||||||
this.chatChannelsManager.find(busData.chat_channel_id).then((channel) => {
|
this.chatChannelsManager.find(busData.chat_channel_id).then((channel) => {
|
||||||
channel.set("status", busData.status);
|
channel.status = busData.status;
|
||||||
|
|
||||||
// it is not possible for the user to set their last read message id
|
// it is not possible for the user to set their last read message id
|
||||||
// if the channel has been archived, because all the messages have
|
// if the channel has been archived, because all the messages have
|
||||||
// been deleted. we don't want them seeing the blue dot anymore so
|
// been deleted. we don't want them seeing the blue dot anymore so
|
||||||
// just completely reset the unreads
|
// just completely reset the unreads
|
||||||
if (busData.status === CHANNEL_STATUSES.archived) {
|
if (busData.status === CHANNEL_STATUSES.archived) {
|
||||||
channel.currentUserMembership.unread_count = 0;
|
channel.currentUserMembership.unreadCount = 0;
|
||||||
channel.currentUserMembership.unread_mentions = 0;
|
channel.currentUserMembership.unreadMentions = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ export default class Chat extends Service {
|
|||||||
const membership = channel.currentUserMembership;
|
const membership = channel.currentUserMembership;
|
||||||
|
|
||||||
if (channel.isDirectMessageChannel) {
|
if (channel.isDirectMessageChannel) {
|
||||||
if (!dmChannelWithUnread && membership.unread_count > 0) {
|
if (!dmChannelWithUnread && membership.unreadCount > 0) {
|
||||||
dmChannelWithUnread = channel.id;
|
dmChannelWithUnread = channel.id;
|
||||||
} else if (!dmChannel) {
|
} else if (!dmChannel) {
|
||||||
dmChannel = channel.id;
|
dmChannel = channel.id;
|
||||||
@ -292,7 +292,7 @@ export default class Chat extends Service {
|
|||||||
if (membership.unread_mentions > 0) {
|
if (membership.unread_mentions > 0) {
|
||||||
publicChannelWithMention = channel.id;
|
publicChannelWithMention = channel.id;
|
||||||
return; // <- We have a public channel with a mention. Break and return this.
|
return; // <- We have a public channel with a mention. Break and return this.
|
||||||
} else if (!publicChannelWithUnread && membership.unread_count > 0) {
|
} else if (!publicChannelWithUnread && membership.unreadCount > 0) {
|
||||||
publicChannelWithUnread = channel.id;
|
publicChannelWithUnread = channel.id;
|
||||||
} else if (
|
} else if (
|
||||||
!defaultChannel &&
|
!defaultChannel &&
|
||||||
|
@ -20,6 +20,7 @@ RSpec.describe "Channel selector modal", type: :system, js: true do
|
|||||||
find("body").send_keys([key_modifier, "k"])
|
find("body").send_keys([key_modifier, "k"])
|
||||||
find("#chat-channel-selector-input").fill_in(with: channel_1.title)
|
find("#chat-channel-selector-input").fill_in(with: channel_1.title)
|
||||||
find(".chat-channel-selection-row[data-id='#{channel_1.id}']").click
|
find(".chat-channel-selection-row[data-id='#{channel_1.id}']").click
|
||||||
|
|
||||||
channel_page.send_message("Hello world")
|
channel_page.send_message("Hello world")
|
||||||
|
|
||||||
expect(channel_page).to have_message(text: "Hello world")
|
expect(channel_page).to have_message(text: "Hello world")
|
||||||
@ -33,6 +34,7 @@ RSpec.describe "Channel selector modal", type: :system, js: true do
|
|||||||
find("body").send_keys([key_modifier, "k"])
|
find("body").send_keys([key_modifier, "k"])
|
||||||
find("#chat-channel-selector-input").fill_in(with: user_1.username)
|
find("#chat-channel-selector-input").fill_in(with: user_1.username)
|
||||||
find(".chat-channel-selection-row[data-id='#{user_1.id}']").click
|
find(".chat-channel-selection-row[data-id='#{user_1.id}']").click
|
||||||
|
|
||||||
channel_page.send_message("Hello world")
|
channel_page.send_message("Hello world")
|
||||||
|
|
||||||
expect(channel_page).to have_message(text: "Hello world")
|
expect(channel_page).to have_message(text: "Hello world")
|
||||||
@ -69,7 +71,6 @@ RSpec.describe "Channel selector modal", type: :system, js: true do
|
|||||||
fab!(:channel_1) { Fabricate(:private_category_channel, group: group_1) }
|
fab!(:channel_1) { Fabricate(:private_category_channel, group: group_1) }
|
||||||
|
|
||||||
it "it doesn’t include limited access channel" do
|
it "it doesn’t include limited access channel" do
|
||||||
chat_page.visit_channel(channel_1)
|
|
||||||
find("body").send_keys([key_modifier, "k"])
|
find("body").send_keys([key_modifier, "k"])
|
||||||
find("#chat-channel-selector-input").fill_in(with: channel_1.title)
|
find("#chat-channel-selector-input").fill_in(with: channel_1.title)
|
||||||
|
|
||||||
|
@ -126,11 +126,11 @@ RSpec.describe "Drawer", type: :system, js: true do
|
|||||||
session.quit
|
session.quit
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(page).to have_content("onlyonce", count: 1)
|
expect(page).to have_content("onlyonce", count: 1, wait: 20)
|
||||||
|
|
||||||
chat_page.visit_channel(channel_2)
|
chat_page.visit_channel(channel_2)
|
||||||
|
|
||||||
expect(page).to have_content("onlyonce", count: 0)
|
expect(page).to have_content("onlyonce", count: 0, wait: 20)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -10,15 +10,13 @@ module("Discourse Chat | Component | chat-channel-card", function (hooks) {
|
|||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(function () {
|
hooks.beforeEach(function () {
|
||||||
this.set("channel", fabricators.chatChannel());
|
this.channel = fabricators.chatChannel();
|
||||||
this.channel.set(
|
this.channel.description =
|
||||||
"description",
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("escapes channel title", async function (assert) {
|
test("escapes channel title", async function (assert) {
|
||||||
this.channel.set("title", "<div class='xss'>evil</div>");
|
this.channel.title = "<div class='xss'>evil</div>";
|
||||||
|
|
||||||
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -26,7 +24,7 @@ module("Discourse Chat | Component | chat-channel-card", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("escapes channel description", async function (assert) {
|
test("escapes channel description", async function (assert) {
|
||||||
this.channel.set("description", "<div class='xss'>evil</div>");
|
this.channel.description = "<div class='xss'>evil</div>";
|
||||||
|
|
||||||
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -34,14 +32,14 @@ module("Discourse Chat | Component | chat-channel-card", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Closed channel", async function (assert) {
|
test("Closed channel", async function (assert) {
|
||||||
this.channel.set("status", "closed");
|
this.channel.status = "closed";
|
||||||
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
assert.true(exists(".chat-channel-card.-closed"));
|
assert.true(exists(".chat-channel-card.-closed"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Archived channel", async function (assert) {
|
test("Archived channel", async function (assert) {
|
||||||
this.channel.set("status", "archived");
|
this.channel.status = "archived";
|
||||||
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
assert.true(exists(".chat-channel-card.-archived"));
|
assert.true(exists(".chat-channel-card.-archived"));
|
||||||
@ -59,7 +57,7 @@ module("Discourse Chat | Component | chat-channel-card", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Joined channel", async function (assert) {
|
test("Joined channel", async function (assert) {
|
||||||
this.channel.currentUserMembership.set("following", true);
|
this.channel.currentUserMembership.following = true;
|
||||||
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
@ -77,7 +75,7 @@ module("Discourse Chat | Component | chat-channel-card", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Memberships count", async function (assert) {
|
test("Memberships count", async function (assert) {
|
||||||
this.channel.set("membershipsCount", 4);
|
this.channel.membershipsCount = 4;
|
||||||
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
@ -87,7 +85,7 @@ module("Discourse Chat | Component | chat-channel-card", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("No description", async function (assert) {
|
test("No description", async function (assert) {
|
||||||
this.channel.set("description", null);
|
this.channel.description = null;
|
||||||
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
assert.false(exists(".chat-channel-card__description"));
|
assert.false(exists(".chat-channel-card__description"));
|
||||||
@ -118,7 +116,7 @@ module("Discourse Chat | Component | chat-channel-card", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Read restricted chatable", async function (assert) {
|
test("Read restricted chatable", async function (assert) {
|
||||||
this.channel.set("chatable.read_restricted", true);
|
this.channel.chatable.read_restricted = true;
|
||||||
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
assert.true(exists(".d-icon-lock"));
|
assert.true(exists(".d-icon-lock"));
|
||||||
|
@ -5,20 +5,15 @@ import hbs from "htmlbars-inline-precompile";
|
|||||||
import pretender from "discourse/tests/helpers/create-pretender";
|
import pretender from "discourse/tests/helpers/create-pretender";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { module, test } from "qunit";
|
import { module, test } from "qunit";
|
||||||
|
import fabricators from "../helpers/fabricators";
|
||||||
|
|
||||||
module("Discourse Chat | Component | chat-channel-leave-btn", function (hooks) {
|
module("Discourse Chat | Component | chat-channel-leave-btn", function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
test("accepts an optional onLeaveChannel callback", async function (assert) {
|
test("accepts an optional onLeaveChannel callback", async function (assert) {
|
||||||
this.set("foo", 1);
|
this.foo = 1;
|
||||||
this.set("onLeaveChannel", () => this.set("foo", 2));
|
this.onLeaveChannel = () => (this.foo = 2);
|
||||||
this.set("channel", {
|
this.channel = fabricators.directMessageChatChannel({ users: [{ id: 1 }] });
|
||||||
id: 1,
|
|
||||||
chatable_type: "DirectMessage",
|
|
||||||
chatable: {
|
|
||||||
users: [{ id: 1 }],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
hbs`<ChatChannelLeaveBtn @channel={{this.channel}} @onLeaveChannel={{this.onLeaveChannel}} />`
|
hbs`<ChatChannelLeaveBtn @channel={{this.channel}} @onLeaveChannel={{this.onLeaveChannel}} />`
|
||||||
@ -34,7 +29,7 @@ module("Discourse Chat | Component | chat-channel-leave-btn", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("has a specific title for direct message channel", async function (assert) {
|
test("has a specific title for direct message channel", async function (assert) {
|
||||||
this.set("channel", { chatable_type: "DirectMessage" });
|
this.channel = fabricators.directMessageChatChannel();
|
||||||
|
|
||||||
await render(hbs`<ChatChannelLeaveBtn @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelLeaveBtn @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -43,7 +38,7 @@ module("Discourse Chat | Component | chat-channel-leave-btn", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("has a specific title for message channel", async function (assert) {
|
test("has a specific title for message channel", async function (assert) {
|
||||||
this.set("channel", { chatable_type: "Topic" });
|
this.channel = fabricators.chatChannel();
|
||||||
|
|
||||||
await render(hbs`<ChatChannelLeaveBtn @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelLeaveBtn @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -53,7 +48,7 @@ module("Discourse Chat | Component | chat-channel-leave-btn", function (hooks) {
|
|||||||
|
|
||||||
test("is not visible on mobile", async function (assert) {
|
test("is not visible on mobile", async function (assert) {
|
||||||
this.site.mobileView = true;
|
this.site.mobileView = true;
|
||||||
this.set("channel", { chatable_type: "Topic" });
|
this.channel = fabricators.chatChannel();
|
||||||
|
|
||||||
await render(hbs`<ChatChannelLeaveBtn @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelLeaveBtn @channel={{this.channel}} />`);
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ module("Discourse Chat | Component | chat-channel-metadata", function (hooks) {
|
|||||||
|
|
||||||
test("unreadIndicator", async function (assert) {
|
test("unreadIndicator", async function (assert) {
|
||||||
this.channel = fabricators.directMessageChatChannel();
|
this.channel = fabricators.directMessageChatChannel();
|
||||||
this.channel.currentUserMembership.unread_count = 1;
|
this.channel.currentUserMembership.unreadCount = 1;
|
||||||
|
|
||||||
this.unreadIndicator = true;
|
this.unreadIndicator = true;
|
||||||
await render(
|
await render(
|
||||||
|
@ -15,10 +15,9 @@ module(
|
|||||||
"channel",
|
"channel",
|
||||||
fabricators.chatChannel({ chatable_type: "Category" })
|
fabricators.chatChannel({ chatable_type: "Category" })
|
||||||
);
|
);
|
||||||
this.channel.setProperties({
|
|
||||||
description: "Important stuff is announced here.",
|
this.channel.description = "Important stuff is announced here.";
|
||||||
title: "announcements",
|
this.channel.title = "announcements";
|
||||||
});
|
|
||||||
this.currentUser.set("has_chat_enabled", true);
|
this.currentUser.set("has_chat_enabled", true);
|
||||||
this.siteSettings.chat_enabled = true;
|
this.siteSettings.chat_enabled = true;
|
||||||
});
|
});
|
||||||
@ -49,7 +48,7 @@ module(
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("no channel description", async function (assert) {
|
test("no channel description", async function (assert) {
|
||||||
this.channel.set("description", null);
|
this.channel.description = null;
|
||||||
|
|
||||||
await render(hbs`<ChatChannelPreviewCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelPreviewCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -83,7 +82,7 @@ module(
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("closed channel", async function (assert) {
|
test("closed channel", async function (assert) {
|
||||||
this.channel.set("status", "closed");
|
this.channel.status = "closed";
|
||||||
await render(hbs`<ChatChannelPreviewCard @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelPreviewCard @channel={{this.channel}} />`);
|
||||||
|
|
||||||
assert.false(
|
assert.false(
|
||||||
|
@ -131,7 +131,7 @@ module("Discourse Chat | Component | chat-channel-row", function (hooks) {
|
|||||||
|
|
||||||
assert.dom(".chat-channel-row").doesNotHaveClass("has-unread");
|
assert.dom(".chat-channel-row").doesNotHaveClass("has-unread");
|
||||||
|
|
||||||
this.categoryChatChannel.currentUserMembership.unread_count = 1;
|
this.categoryChatChannel.currentUserMembership.unreadCount = 1;
|
||||||
|
|
||||||
await render(hbs`<ChatChannelRow @channel={{this.categoryChatChannel}} />`);
|
await render(hbs`<ChatChannelRow @channel={{this.categoryChatChannel}} />`);
|
||||||
|
|
||||||
|
@ -10,12 +10,9 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
test("category channel", async function (assert) {
|
test("category channel", async function (assert) {
|
||||||
this.set(
|
this.channel = fabricators.chatChannel({
|
||||||
"channel",
|
|
||||||
fabricators.chatChannel({
|
|
||||||
chatable_type: CHATABLE_TYPES.categoryChannel,
|
chatable_type: CHATABLE_TYPES.categoryChannel,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -30,13 +27,10 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("category channel - escapes title", async function (assert) {
|
test("category channel - escapes title", async function (assert) {
|
||||||
this.set(
|
this.channel = fabricators.chatChannel({
|
||||||
"channel",
|
|
||||||
fabricators.chatChannel({
|
|
||||||
chatable_type: CHATABLE_TYPES.categoryChannel,
|
chatable_type: CHATABLE_TYPES.categoryChannel,
|
||||||
title: "<div class='xss'>evil</div>",
|
title: "<div class='xss'>evil</div>",
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -44,13 +38,10 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("category channel - read restricted", async function (assert) {
|
test("category channel - read restricted", async function (assert) {
|
||||||
this.set(
|
this.channel = fabricators.chatChannel({
|
||||||
"channel",
|
|
||||||
fabricators.chatChannel({
|
|
||||||
chatable_type: CHATABLE_TYPES.categoryChannel,
|
chatable_type: CHATABLE_TYPES.categoryChannel,
|
||||||
chatable: { read_restricted: true },
|
chatable: { read_restricted: true },
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -58,13 +49,10 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("category channel - not read restricted", async function (assert) {
|
test("category channel - not read restricted", async function (assert) {
|
||||||
this.set(
|
this.channel = fabricators.chatChannel({
|
||||||
"channel",
|
|
||||||
fabricators.chatChannel({
|
|
||||||
chatable_type: CHATABLE_TYPES.categoryChannel,
|
chatable_type: CHATABLE_TYPES.categoryChannel,
|
||||||
chatable: { read_restricted: false },
|
chatable: { read_restricted: false },
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -72,7 +60,7 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("direct message channel - one user", async function (assert) {
|
test("direct message channel - one user", async function (assert) {
|
||||||
this.set("channel", fabricators.directMessageChatChannel());
|
this.channel = fabricators.directMessageChatChannel();
|
||||||
|
|
||||||
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
||||||
|
|
||||||
@ -98,7 +86,7 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||||||
avatar_template: "/letter_avatar_proxy/v3/letter/t/31188e/{size}.png",
|
avatar_template: "/letter_avatar_proxy/v3/letter/t/31188e/{size}.png",
|
||||||
});
|
});
|
||||||
|
|
||||||
this.set("channel", channel);
|
this.channel = channel;
|
||||||
|
|
||||||
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
|
|||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
function generateMessageProps(messageData = {}) {
|
function generateMessageProps(messageData = {}) {
|
||||||
const chatChannel = ChatChannel.create({
|
const channel = ChatChannel.create({
|
||||||
chatable: { id: 1 },
|
chatable_id: 1,
|
||||||
chatable_type: "Category",
|
chatable_type: "Category",
|
||||||
id: 9,
|
id: 9,
|
||||||
title: "Site",
|
title: "Site",
|
||||||
@ -21,15 +21,15 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
|
|||||||
unread_count: 0,
|
unread_count: 0,
|
||||||
muted: false,
|
muted: false,
|
||||||
},
|
},
|
||||||
canDeleteSelf: true,
|
can_delete_self: true,
|
||||||
canDeleteOthers: true,
|
can_delete_others: true,
|
||||||
canFlag: true,
|
can_flag: true,
|
||||||
userSilenced: false,
|
user_silenced: false,
|
||||||
canModerate: true,
|
can_moderate: true,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
message: ChatMessage.create(
|
message: ChatMessage.create(
|
||||||
chatChannel,
|
channel,
|
||||||
Object.assign(
|
Object.assign(
|
||||||
{
|
{
|
||||||
id: 178,
|
id: 178,
|
||||||
@ -44,7 +44,7 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
|
|||||||
messageData
|
messageData
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
chatChannel,
|
channel,
|
||||||
afterExpand: () => {},
|
afterExpand: () => {},
|
||||||
onHoverMessage: () => {},
|
onHoverMessage: () => {},
|
||||||
messageDidEnterViewport: () => {},
|
messageDidEnterViewport: () => {},
|
||||||
@ -55,7 +55,7 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
|
|||||||
const template = hbs`
|
const template = hbs`
|
||||||
<ChatMessage
|
<ChatMessage
|
||||||
@message={{this.message}}
|
@message={{this.message}}
|
||||||
@channel={{this.chatChannel}}
|
@channel={{this.channel}}
|
||||||
@messageDidEnterViewport={{this.messageDidEnterViewport}}
|
@messageDidEnterViewport={{this.messageDidEnterViewport}}
|
||||||
@messageDidLeaveViewport={{this.messageDidLeaveViewport}}
|
@messageDidLeaveViewport={{this.messageDidLeaveViewport}}
|
||||||
/>
|
/>
|
||||||
@ -64,6 +64,7 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
|
|||||||
test("Message with edits", async function (assert) {
|
test("Message with edits", async function (assert) {
|
||||||
this.setProperties(generateMessageProps({ edited: true }));
|
this.setProperties(generateMessageProps({ edited: true }));
|
||||||
await render(template);
|
await render(template);
|
||||||
|
|
||||||
assert.true(
|
assert.true(
|
||||||
exists(".chat-message-edited"),
|
exists(".chat-message-edited"),
|
||||||
"has the correct edited css class"
|
"has the correct edited css class"
|
||||||
@ -83,6 +84,7 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
|
|||||||
test("Hidden message", async function (assert) {
|
test("Hidden message", async function (assert) {
|
||||||
this.setProperties(generateMessageProps({ hidden: true }));
|
this.setProperties(generateMessageProps({ hidden: true }));
|
||||||
await render(template);
|
await render(template);
|
||||||
|
|
||||||
assert.true(
|
assert.true(
|
||||||
exists(".chat-message-hidden .chat-message-expand"),
|
exists(".chat-message-hidden .chat-message-expand"),
|
||||||
"has the correct hidden css class and expand button within"
|
"has the correct hidden css class and expand button within"
|
||||||
|
@ -2,9 +2,9 @@ import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
|||||||
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
||||||
import hbs from "htmlbars-inline-precompile";
|
import hbs from "htmlbars-inline-precompile";
|
||||||
import fabricators from "../helpers/fabricators";
|
import fabricators from "../helpers/fabricators";
|
||||||
import MockPresenceChannel from "../helpers/mock-presence-channel";
|
|
||||||
import { module, test } from "qunit";
|
import { module, test } from "qunit";
|
||||||
import { render } from "@ember/test-helpers";
|
import { render, settled } from "@ember/test-helpers";
|
||||||
|
import { joinChannel } from "discourse/tests/helpers/presence-pretender";
|
||||||
|
|
||||||
module(
|
module(
|
||||||
"Discourse Chat | Component | chat-replying-indicator",
|
"Discourse Chat | Component | chat-replying-indicator",
|
||||||
@ -12,155 +12,148 @@ module(
|
|||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
test("not displayed when no one is replying", async function (assert) {
|
test("not displayed when no one is replying", async function (assert) {
|
||||||
this.set("chatChannel", fabricators.chatChannel());
|
this.channel = fabricators.chatChannel();
|
||||||
this.set(
|
|
||||||
"presenceChannel",
|
|
||||||
MockPresenceChannel.create({
|
|
||||||
name: `/chat-reply/${this.chatChannel.id}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(
|
await render(hbs`<ChatReplyingIndicator @channel={{this.channel}} />`);
|
||||||
hbs`<ChatReplyingIndicator @presenceChannel={{this.presenceChannel}} @chatChannel={{this.chatChannel}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.false(exists(".chat-replying-indicator__text"));
|
assert.false(exists(".chat-replying-indicator__text"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("displays indicator when user is replying", async function (assert) {
|
test("displays indicator when user is replying", async function (assert) {
|
||||||
this.set("chatChannel", fabricators.chatChannel());
|
this.channel = fabricators.chatChannel();
|
||||||
this.set(
|
|
||||||
"presenceChannel",
|
|
||||||
MockPresenceChannel.create({
|
|
||||||
name: `/chat-reply/${this.chatChannel.id}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(
|
await render(hbs`<ChatReplyingIndicator @channel={{this.channel}} />`);
|
||||||
hbs`<ChatReplyingIndicator @presenceChannel={{this.presenceChannel}} @chatChannel={{this.chatChannel}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
const sam = { id: 1, username: "sam" };
|
await joinChannel("/chat-reply/1", {
|
||||||
this.set("presenceChannel.users", [sam]);
|
id: 1,
|
||||||
|
avatar_template: "/images/avatar.png",
|
||||||
|
username: "sam",
|
||||||
|
});
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(".chat-replying-indicator__text").innerText,
|
query(".chat-replying-indicator__text").innerText,
|
||||||
`${sam.username} is typing`
|
`sam is typing`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("displays indicator when 2 or 3 users are replying", async function (assert) {
|
test("displays indicator when 2 or 3 users are replying", async function (assert) {
|
||||||
this.set("chatChannel", fabricators.chatChannel());
|
this.channel = fabricators.chatChannel();
|
||||||
this.set(
|
|
||||||
"presenceChannel",
|
|
||||||
MockPresenceChannel.create({
|
|
||||||
name: `/chat-reply/${this.chatChannel.id}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(
|
await render(hbs`<ChatReplyingIndicator @channel={{this.channel}} />`);
|
||||||
hbs`<ChatReplyingIndicator @presenceChannel={{this.presenceChannel}} @chatChannel={{this.chatChannel}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
const sam = { id: 1, username: "sam" };
|
await joinChannel("/chat-reply/1", {
|
||||||
const mark = { id: 2, username: "mark" };
|
id: 1,
|
||||||
this.set("presenceChannel.users", [sam, mark]);
|
avatar_template: "/images/avatar.png",
|
||||||
|
username: "sam",
|
||||||
|
});
|
||||||
|
|
||||||
|
await joinChannel("/chat-reply/1", {
|
||||||
|
id: 2,
|
||||||
|
avatar_template: "/images/avatar.png",
|
||||||
|
username: "mark",
|
||||||
|
});
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(".chat-replying-indicator__text").innerText,
|
query(".chat-replying-indicator__text").innerText,
|
||||||
`${sam.username} and ${mark.username} are typing`
|
`sam and mark are typing`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("displays indicator when 3 users are replying", async function (assert) {
|
test("displays indicator when 3 users are replying", async function (assert) {
|
||||||
this.set("chatChannel", fabricators.chatChannel());
|
this.channel = fabricators.chatChannel();
|
||||||
this.set(
|
|
||||||
"presenceChannel",
|
|
||||||
MockPresenceChannel.create({
|
|
||||||
name: `/chat-reply/${this.chatChannel.id}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(
|
await render(hbs`<ChatReplyingIndicator @channel={{this.channel}} />`);
|
||||||
hbs`<ChatReplyingIndicator @presenceChannel={{this.presenceChannel}} @chatChannel={{this.chatChannel}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
const sam = { id: 1, username: "sam" };
|
await joinChannel("/chat-reply/1", {
|
||||||
const mark = { id: 2, username: "mark" };
|
id: 1,
|
||||||
const joffrey = { id: 3, username: "joffrey" };
|
avatar_template: "/images/avatar.png",
|
||||||
this.set("presenceChannel.users", [sam, mark, joffrey]);
|
username: "sam",
|
||||||
|
});
|
||||||
|
|
||||||
|
await joinChannel("/chat-reply/1", {
|
||||||
|
id: 2,
|
||||||
|
avatar_template: "/images/avatar.png",
|
||||||
|
username: "mark",
|
||||||
|
});
|
||||||
|
|
||||||
|
await joinChannel("/chat-reply/1", {
|
||||||
|
id: 3,
|
||||||
|
avatar_template: "/images/avatar.png",
|
||||||
|
username: "joffrey",
|
||||||
|
});
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(".chat-replying-indicator__text").innerText,
|
query(".chat-replying-indicator__text").innerText,
|
||||||
`${sam.username}, ${mark.username} and ${joffrey.username} are typing`
|
`sam, mark and joffrey are typing`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("displays indicator when more than 3 users are replying", async function (assert) {
|
test("displays indicator when more than 3 users are replying", async function (assert) {
|
||||||
this.set("chatChannel", fabricators.chatChannel());
|
this.channel = fabricators.chatChannel();
|
||||||
this.set(
|
|
||||||
"presenceChannel",
|
|
||||||
MockPresenceChannel.create({
|
|
||||||
name: `/chat-reply/${this.chatChannel.id}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(
|
await render(hbs`<ChatReplyingIndicator @channel={{this.channel}} />`);
|
||||||
hbs`<ChatReplyingIndicator @presenceChannel={{this.presenceChannel}} @chatChannel={{this.chatChannel}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
const sam = { id: 1, username: "sam" };
|
await joinChannel("/chat-reply/1", {
|
||||||
const mark = { id: 2, username: "mark" };
|
id: 1,
|
||||||
const joffrey = { id: 3, username: "joffrey" };
|
avatar_template: "/images/avatar.png",
|
||||||
const taylor = { id: 4, username: "taylor" };
|
username: "sam",
|
||||||
this.set("presenceChannel.users", [sam, mark, joffrey, taylor]);
|
});
|
||||||
|
|
||||||
|
await joinChannel("/chat-reply/1", {
|
||||||
|
id: 2,
|
||||||
|
avatar_template: "/images/avatar.png",
|
||||||
|
username: "mark",
|
||||||
|
});
|
||||||
|
|
||||||
|
await joinChannel("/chat-reply/1", {
|
||||||
|
id: 3,
|
||||||
|
avatar_template: "/images/avatar.png",
|
||||||
|
username: "joffrey",
|
||||||
|
});
|
||||||
|
|
||||||
|
await joinChannel("/chat-reply/1", {
|
||||||
|
id: 4,
|
||||||
|
avatar_template: "/images/avatar.png",
|
||||||
|
username: "taylor",
|
||||||
|
});
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(".chat-replying-indicator__text").innerText,
|
query(".chat-replying-indicator__text").innerText,
|
||||||
`${sam.username}, ${mark.username} and 2 others are typing`
|
`sam, mark and 2 others are typing`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("filters current user from list of repliers", async function (assert) {
|
test("filters current user from list of repliers", async function (assert) {
|
||||||
this.set("chatChannel", fabricators.chatChannel());
|
this.channel = fabricators.chatChannel();
|
||||||
this.set(
|
|
||||||
"presenceChannel",
|
|
||||||
MockPresenceChannel.create({
|
|
||||||
name: `/chat-reply/${this.chatChannel.id}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(
|
await render(hbs`<ChatReplyingIndicator @channel={{this.channel}} />`);
|
||||||
hbs`<ChatReplyingIndicator @presenceChannel={{this.presenceChannel}} @chatChannel={{this.chatChannel}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
const sam = { id: 1, username: "sam" };
|
await joinChannel("/chat-reply/1", {
|
||||||
this.set("presenceChannel.users", [sam, this.currentUser]);
|
id: 1,
|
||||||
|
avatar_template: "/images/avatar.png",
|
||||||
|
username: "sam",
|
||||||
|
});
|
||||||
|
|
||||||
|
await joinChannel("/chat-reply/1", this.currentUser);
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(".chat-replying-indicator__text").innerText,
|
query(".chat-replying-indicator__text").innerText,
|
||||||
`${sam.username} is typing`
|
`sam is typing`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("resets presence when channel is draft", async function (assert) {
|
test("resets presence when channel is draft", async function (assert) {
|
||||||
this.set("chatChannel", fabricators.chatChannel());
|
this.channel = fabricators.chatChannel();
|
||||||
this.set(
|
|
||||||
"presenceChannel",
|
|
||||||
MockPresenceChannel.create({
|
|
||||||
name: `/chat-reply/${this.chatChannel.id}`,
|
|
||||||
subscribed: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(
|
await render(hbs`<ChatReplyingIndicator @channel={{this.channel}} />`);
|
||||||
hbs`<ChatReplyingIndicator @presenceChannel={{this.presenceChannel}} @chatChannel={{this.chatChannel}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.true(this.presenceChannel.subscribed);
|
assert.dom(".chat-replying-indicator.is-subscribed").exists();
|
||||||
|
|
||||||
this.set("chatChannel", fabricators.chatChannel({ isDraft: true }));
|
this.channel.isDraft = true;
|
||||||
assert.false(this.presenceChannel.subscribed);
|
|
||||||
|
await settled();
|
||||||
|
|
||||||
|
assert.dom(".chat-replying-indicator.is-subscribed").doesNotExist();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
|
||||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||||
import hbs from "htmlbars-inline-precompile";
|
import hbs from "htmlbars-inline-precompile";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { module, test } from "qunit";
|
import { module, test } from "qunit";
|
||||||
import { render } from "@ember/test-helpers";
|
import { render } from "@ember/test-helpers";
|
||||||
|
import fabricators from "../helpers/fabricators";
|
||||||
|
|
||||||
module(
|
module(
|
||||||
"Discourse Chat | Component | chat-retention-reminder-text",
|
"Discourse Chat | Component | chat-retention-reminder-text",
|
||||||
@ -11,7 +11,7 @@ module(
|
|||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
test("when setting is set on 0", async function (assert) {
|
test("when setting is set on 0", async function (assert) {
|
||||||
this.channel = ChatChannel.create({ chatable_type: "Category" });
|
this.channel = fabricators.chatChannel();
|
||||||
this.siteSettings.chat_channel_retention_days = 0;
|
this.siteSettings.chat_channel_retention_days = 0;
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
@ -25,7 +25,7 @@ module(
|
|||||||
|
|
||||||
test("when channel is a public channel", async function (assert) {
|
test("when channel is a public channel", async function (assert) {
|
||||||
const count = 10;
|
const count = 10;
|
||||||
this.channel = ChatChannel.create({ chatable_type: "Category" });
|
this.channel = fabricators.chatChannel();
|
||||||
this.siteSettings.chat_channel_retention_days = count;
|
this.siteSettings.chat_channel_retention_days = count;
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
@ -39,9 +39,7 @@ module(
|
|||||||
|
|
||||||
test("when channel is a DM channel", async function (assert) {
|
test("when channel is a DM channel", async function (assert) {
|
||||||
const count = 10;
|
const count = 10;
|
||||||
this.channel = ChatChannel.create({
|
this.channel = fabricators.directMessageChatChannel();
|
||||||
chatable_type: "DirectMessage",
|
|
||||||
});
|
|
||||||
this.siteSettings.chat_dm_retention_days = count;
|
this.siteSettings.chat_dm_retention_days = count;
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
|
@ -2,7 +2,7 @@ import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
|||||||
import { click, fillIn, render } from "@ember/test-helpers";
|
import { click, fillIn, render } from "@ember/test-helpers";
|
||||||
import hbs from "htmlbars-inline-precompile";
|
import hbs from "htmlbars-inline-precompile";
|
||||||
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
||||||
import { createDirectMessageChannelDraft } from "discourse/plugins/chat/discourse/models/chat-channel";
|
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||||
import { Promise } from "rsvp";
|
import { Promise } from "rsvp";
|
||||||
import fabricators from "../helpers/fabricators";
|
import fabricators from "../helpers/fabricators";
|
||||||
import { module, test } from "qunit";
|
import { module, test } from "qunit";
|
||||||
@ -25,7 +25,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||||||
|
|
||||||
test("search", async function (assert) {
|
test("search", async function (assert) {
|
||||||
this.set("chat", mockChat(this));
|
this.set("chat", mockChat(this));
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
||||||
@ -37,7 +37,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||||||
|
|
||||||
test("select/deselect", async function (assert) {
|
test("select/deselect", async function (assert) {
|
||||||
this.set("chat", mockChat(this));
|
this.set("chat", mockChat(this));
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
||||||
@ -54,7 +54,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||||||
|
|
||||||
test("no search results", async function (assert) {
|
test("no search results", async function (assert) {
|
||||||
this.set("chat", mockChat(this, { users: [] }));
|
this.set("chat", mockChat(this, { users: [] }));
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
||||||
@ -66,7 +66,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||||||
|
|
||||||
test("loads user on first load", async function (assert) {
|
test("loads user on first load", async function (assert) {
|
||||||
this.set("chat", mockChat(this));
|
this.set("chat", mockChat(this));
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
||||||
@ -78,7 +78,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||||||
|
|
||||||
test("do not load more users after selection", async function (assert) {
|
test("do not load more users after selection", async function (assert) {
|
||||||
this.set("chat", mockChat(this));
|
this.set("chat", mockChat(this));
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
||||||
@ -90,7 +90,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||||||
|
|
||||||
test("apply is-focused to filter-area on focus input", async function (assert) {
|
test("apply is-focused to filter-area on focus input", async function (assert) {
|
||||||
this.set("chat", mockChat(this));
|
this.set("chat", mockChat(this));
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} /><button class="test-blur">blur</button>`
|
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} /><button class="test-blur">blur</button>`
|
||||||
@ -105,7 +105,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||||||
|
|
||||||
test("state is reset on channel change", async function (assert) {
|
test("state is reset on channel change", async function (assert) {
|
||||||
this.set("chat", mockChat(this));
|
this.set("chat", mockChat(this));
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
||||||
@ -115,7 +115,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||||||
assert.strictEqual(query(".filter-usernames").value, "hawk");
|
assert.strictEqual(query(".filter-usernames").value, "hawk");
|
||||||
|
|
||||||
this.set("channel", fabricators.chatChannel());
|
this.set("channel", fabricators.chatChannel());
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
|
|
||||||
assert.strictEqual(query(".filter-usernames").value, "");
|
assert.strictEqual(query(".filter-usernames").value, "");
|
||||||
assert.true(exists(".filter-area.is-focused"));
|
assert.true(exists(".filter-area.is-focused"));
|
||||||
@ -129,7 +129,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||||||
};
|
};
|
||||||
const chat = mockChat(this, { users: [userWithStatus] });
|
const chat = mockChat(this, { users: [userWithStatus] });
|
||||||
this.set("chat", chat);
|
this.set("chat", chat);
|
||||||
this.set("channel", createDirectMessageChannelDraft());
|
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
hbs`<DirectMessageCreator @channel={{this.channel}} @chat={{this.chat}} />`
|
||||||
|
@ -138,7 +138,7 @@ export function directMessageChannelPretender(
|
|||||||
opts = { unread_count: 0, muted: false }
|
opts = { unread_count: 0, muted: false }
|
||||||
) {
|
) {
|
||||||
let copy = cloneJSON(directMessageChannels[0]);
|
let copy = cloneJSON(directMessageChannels[0]);
|
||||||
copy.chat_channel.currentUserMembership.unread_count = opts.unread_count;
|
copy.chat_channel.currentUserMembership.unreadCount = opts.unread_count;
|
||||||
copy.chat_channel.currentUserMembership.muted = opts.muted;
|
copy.chat_channel.currentUserMembership.muted = opts.muted;
|
||||||
server.get("/chat/chat_channels/75.json", () => helper.response(copy));
|
server.get("/chat/chat_channels/75.json", () => helper.response(copy));
|
||||||
}
|
}
|
||||||
@ -150,13 +150,13 @@ export function chatChannelPretender(server, helper, changes = []) {
|
|||||||
let found;
|
let found;
|
||||||
found = copy.public_channels.find((c) => c.id === change.id);
|
found = copy.public_channels.find((c) => c.id === change.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
found.currentUserMembership.unread_count = change.unread_count;
|
found.currentUserMembership.unreadCount = change.unread_count;
|
||||||
found.currentUserMembership.muted = change.muted;
|
found.currentUserMembership.muted = change.muted;
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
found = copy.direct_message_channels.find((c) => c.id === change.id);
|
found = copy.direct_message_channels.find((c) => c.id === change.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
found.currentUserMembership.unread_count = change.unread_count;
|
found.currentUserMembership.unreadCount = change.unread_count;
|
||||||
found.currentUserMembership.muted = change.muted;
|
found.currentUserMembership.muted = change.muted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import EmberObject from "@ember/object";
|
|
||||||
|
|
||||||
export default class MockPresenceChannel extends EmberObject {
|
|
||||||
users = [];
|
|
||||||
name = null;
|
|
||||||
subscribed = false;
|
|
||||||
|
|
||||||
async unsubscribe() {
|
|
||||||
this.set("subscribed", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
async subscribe() {
|
|
||||||
this.set("subscribed", true);
|
|
||||||
}
|
|
||||||
}
|
|
@ -83,12 +83,12 @@ acceptance("Discourse Chat | Unit | Service | chat-guardian", function (needs) {
|
|||||||
set(this.currentUser, "admin", true);
|
set(this.currentUser, "admin", true);
|
||||||
set(this.currentUser, "moderator", true);
|
set(this.currentUser, "moderator", true);
|
||||||
|
|
||||||
channel.set("status", "read_only");
|
channel.status = "read_only";
|
||||||
assert.notOk(this.chatGuardian.canArchiveChannel(channel));
|
assert.notOk(this.chatGuardian.canArchiveChannel(channel));
|
||||||
channel.set("status", "open");
|
channel.status = "open";
|
||||||
|
|
||||||
channel.set("status", "archived");
|
channel.status = "archived";
|
||||||
assert.notOk(this.chatGuardian.canArchiveChannel(channel));
|
assert.notOk(this.chatGuardian.canArchiveChannel(channel));
|
||||||
channel.set("status", "open");
|
channel.status = "open";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user