DEV: various improvements to devex on chat (#21612)
- Improves styleguide support - Adds toggle color scheme to styleguide - Adds properties mutators to styleguide - Attempts to quit a session as soon as done with it in system specs, this should at least free resources faster - Refactors fabricators to simplify them - Adds more fabricators (uploads for example) - Starts implementing components pattern in system specs - Uses Chat::Message creator to create messages in system specs, this should help to have more real specs as the side effects should now happen
This commit is contained in:
parent
4d0b997559
commit
60c67afba4
|
@ -41,7 +41,6 @@
|
|||
{{#each @channel.messages key="id" as |message|}}
|
||||
<ChatMessage
|
||||
@message={{message}}
|
||||
@channel={{@channel}}
|
||||
@resendStagedMessage={{this.resendStagedMessage}}
|
||||
@messageDidEnterViewport={{this.messageDidEnterViewport}}
|
||||
@messageDidLeaveViewport={{this.messageDidLeaveViewport}}
|
||||
|
|
|
@ -113,11 +113,10 @@ export default class ChatLivePane extends Component {
|
|||
if (this._loadedChannelId !== this.args.channel?.id) {
|
||||
this.unsubscribeToUpdates(this._loadedChannelId);
|
||||
this.chatChannelPane.selectingMessages = false;
|
||||
this.chatChannelComposer.message =
|
||||
this.args.channel.draft ||
|
||||
ChatMessage.createDraftMessage(this.args.channel, {
|
||||
user: this.currentUser,
|
||||
});
|
||||
|
||||
if (this.args.channel.draft) {
|
||||
this.chatChannelComposer.message = this.args.channel.draft;
|
||||
}
|
||||
|
||||
this._loadedChannelId = this.args.channel?.id;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="chat-composer-message-details">
|
||||
<div class="chat-composer-message-details" data-id={{@message.id}}>
|
||||
<div class="chat-reply">
|
||||
{{d-icon @icon}}
|
||||
{{d-icon (if @message.editing "pencil-alt" "reply")}}
|
||||
<ChatUserAvatar @user={{@message.user}} />
|
||||
<span class="chat-reply__username">{{@message.user.username}}</span>
|
||||
<span class="chat-reply__excerpt">
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
this.currentMessage
|
||||
this.currentMessage.inReplyTo
|
||||
}}
|
||||
@icon={{if this.currentMessage.editing "pencil-alt" "reply"}}
|
||||
@cancelAction={{this.onCancel}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
@ -26,7 +25,7 @@
|
|||
}}
|
||||
{{did-update this.didUpdateMessage this.currentMessage}}
|
||||
{{did-update this.didUpdateInReplyTo this.currentMessage.inReplyTo}}
|
||||
{{did-insert this.setupAppEvents}}
|
||||
{{did-insert this.setup}}
|
||||
{{will-destroy this.teardown}}
|
||||
{{will-destroy this.cancelPersistDraft}}
|
||||
>
|
||||
|
@ -71,7 +70,7 @@
|
|||
{{on "click" this.onSend}}
|
||||
@icon="paper-plane"
|
||||
class="chat-composer__send-btn"
|
||||
title="chat.composer.send"
|
||||
title={{i18n "chat.composer.send"}}
|
||||
disabled={{or this.disabled (not this.sendEnabled)}}
|
||||
tabindex={{if this.sendEnabled 0 -1}}
|
||||
{{on "focus" (fn this.computeIsFocused true)}}
|
||||
|
|
|
@ -141,7 +141,10 @@ export default class ChatComposer extends Component {
|
|||
}
|
||||
|
||||
@action
|
||||
setupAppEvents() {
|
||||
setup() {
|
||||
this.composer.message = ChatMessage.createDraftMessage(this.args.channel, {
|
||||
user: this.currentUser,
|
||||
});
|
||||
this.appEvents.on("chat:modify-selection", this, "modifySelection");
|
||||
this.appEvents.on(
|
||||
"chat:open-insert-link-modal",
|
||||
|
|
|
@ -54,6 +54,10 @@ export default class ChatMessageActionsDesktop extends Component {
|
|||
this.context
|
||||
);
|
||||
|
||||
if (!messageContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewport = messageContainer.closest(".popper-viewport");
|
||||
this.size =
|
||||
viewport.clientWidth < REDUCED_WIDTH_THRESHOLD ? REDUCED : FULL;
|
||||
|
|
|
@ -130,7 +130,7 @@ export default class ChatMessage extends Component {
|
|||
}
|
||||
|
||||
_chatMessageDecorators.forEach((decorator) => {
|
||||
decorator.call(this, this.messageContainer, this.args.channel);
|
||||
decorator.call(this, this.messageContainer, this.args.message.channel);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ export default class ChatMessage extends Component {
|
|||
!this.args.message?.deletedAt ||
|
||||
this.currentUser.id === this.args.message?.user?.id ||
|
||||
this.currentUser.staff ||
|
||||
this.args.channel?.canModerate
|
||||
this.args.message?.channel?.canModerate
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -316,7 +316,10 @@ export default class ChatMessage extends Component {
|
|||
}
|
||||
|
||||
get threadingEnabled() {
|
||||
return this.args.channel?.threadingEnabled && !!this.args.message?.thread;
|
||||
return (
|
||||
this.args.message?.channel?.threadingEnabled &&
|
||||
!!this.args.message?.thread
|
||||
);
|
||||
}
|
||||
|
||||
get showThreadIndicator() {
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
{{#each this.thread.messages key="id" as |message|}}
|
||||
<ChatMessage
|
||||
@message={{message}}
|
||||
@channel={{this.channel}}
|
||||
@resendStagedMessage={{this.resendStagedMessage}}
|
||||
@messageDidEnterViewport={{this.messageDidEnterViewport}}
|
||||
@messageDidLeaveViewport={{this.messageDidLeaveViewport}}
|
||||
|
|
|
@ -20,10 +20,7 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Chat::Thread::OriginalMessage
|
||||
@thread={{@thread}}
|
||||
@message={{@thread.originalMessage}}
|
||||
/>
|
||||
<Chat::Thread::OriginalMessage @message={{@thread.originalMessage}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,15 @@
|
|||
<StyleguideExample @title="<ChatComposerMessageDetails>">
|
||||
<Styleguide::Component>
|
||||
<ChatComposerMessageDetails @message={{this.message}} />
|
||||
</Styleguide::Component>
|
||||
|
||||
<Styleguide::Controls>
|
||||
<Styleguide::Controls::Row @name="Mode">
|
||||
{{#if this.message.editing}}
|
||||
<DButton @action={{this.toggleMode}} @translatedLabel="Reply" />
|
||||
{{else}}
|
||||
<DButton @action={{this.toggleMode}} @translatedLabel="Editing" />
|
||||
{{/if}}
|
||||
</Styleguide::Controls::Row>
|
||||
</Styleguide::Controls>
|
||||
</StyleguideExample>
|
|
@ -0,0 +1,27 @@
|
|||
import Component from "@glimmer/component";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { action } from "@ember/object";
|
||||
import { cached } from "@glimmer/tracking";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default class ChatStyleguideChatComposerMessageDetails extends Component {
|
||||
@service site;
|
||||
@service session;
|
||||
@service keyValueStore;
|
||||
|
||||
@cached
|
||||
get message() {
|
||||
return fabricators.message();
|
||||
}
|
||||
|
||||
@action
|
||||
toggleMode() {
|
||||
if (this.message.editing) {
|
||||
this.message.editing = false;
|
||||
this.message.inReplyTo = fabricators.message();
|
||||
} else {
|
||||
this.message.editing = true;
|
||||
this.message.inReplyTo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<StyleguideExample @title="<ChatComposer>">
|
||||
<Styleguide::Component>
|
||||
<Chat::Composer::Channel
|
||||
@channel={{this.channel}}
|
||||
@onSendMessage={{this.onSendMessage}}
|
||||
/>
|
||||
</Styleguide::Component>
|
||||
|
||||
<Styleguide::Controls>
|
||||
<Styleguide::Controls::Row @name="Disabled">
|
||||
<DToggleSwitch
|
||||
@state={{this.channel.isReadOnly}}
|
||||
{{on "click" this.toggleDisabled}}
|
||||
/>
|
||||
</Styleguide::Controls::Row>
|
||||
<Styleguide::Controls::Row @name="Sending">
|
||||
<DToggleSwitch
|
||||
@state={{this.chatChannelPane.sending}}
|
||||
{{on "click" this.toggleSending}}
|
||||
/>
|
||||
</Styleguide::Controls::Row>
|
||||
</Styleguide::Controls>
|
||||
</StyleguideExample>
|
|
@ -0,0 +1,30 @@
|
|||
import Component from "@glimmer/component";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { CHANNEL_STATUSES } from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||
|
||||
export default class ChatStyleguideChatComposer extends Component {
|
||||
@service chatChannelComposer;
|
||||
@service chatChannelPane;
|
||||
|
||||
channel = fabricators.channel();
|
||||
|
||||
@action
|
||||
toggleDisabled() {
|
||||
if (this.channel.status === CHANNEL_STATUSES.open) {
|
||||
this.channel.status = CHANNEL_STATUSES.readOnly;
|
||||
} else {
|
||||
this.channel.status = CHANNEL_STATUSES.open;
|
||||
}
|
||||
}
|
||||
@action
|
||||
toggleSending() {
|
||||
this.chatChannelPane.sending = !this.chatChannelPane.sending;
|
||||
}
|
||||
|
||||
@action
|
||||
onSendMessage() {
|
||||
this.chatChannelComposer.reset();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<StyleguideExample @title="<ChatMessage>">
|
||||
<Styleguide::Component>
|
||||
<ChatMessage
|
||||
@message={{this.message}}
|
||||
@context="channel"
|
||||
@messageDidEnterViewport={{(noop)}}
|
||||
@messageDidLeaveViewport={{(noop)}}
|
||||
/>
|
||||
</Styleguide::Component>
|
||||
|
||||
<Styleguide::Controls>
|
||||
<Styleguide::Controls::Row @name="Last Visit">
|
||||
<DToggleSwitch
|
||||
@state={{this.message.newest}}
|
||||
{{on "click" this.toggleLastVisit}}
|
||||
/>
|
||||
</Styleguide::Controls::Row>
|
||||
<Styleguide::Controls::Row @name="Deleted">
|
||||
<DToggleSwitch
|
||||
@state={{not (not this.message.deletedAt)}}
|
||||
{{on "click" this.toggleDeleted}}
|
||||
/>
|
||||
</Styleguide::Controls::Row>
|
||||
<Styleguide::Controls::Row @name="Bookmark">
|
||||
<DToggleSwitch
|
||||
@state={{not (not this.message.bookmark)}}
|
||||
{{on "click" this.toggleBookmarked}}
|
||||
/>
|
||||
</Styleguide::Controls::Row>
|
||||
<Styleguide::Controls::Row @name="Thread">
|
||||
<DToggleSwitch
|
||||
@state={{not (not this.message.thread)}}
|
||||
{{on "click" this.toggleThread}}
|
||||
/>
|
||||
</Styleguide::Controls::Row>
|
||||
<Styleguide::Controls::Row @name="Reactions">
|
||||
<DToggleSwitch
|
||||
@state={{not (not this.message.reactions)}}
|
||||
{{on "click" this.toggleReaction}}
|
||||
/>
|
||||
</Styleguide::Controls::Row>
|
||||
<Styleguide::Controls::Row @name="Upload">
|
||||
<DToggleSwitch
|
||||
@state={{not (not this.message.uploads)}}
|
||||
{{on "click" this.toggleUpload}}
|
||||
/>
|
||||
</Styleguide::Controls::Row>
|
||||
<Styleguide::Controls::Row @name="Message">
|
||||
<textarea
|
||||
{{on "input" this.updateMessage}}
|
||||
>{{this.message.message}}</textarea>
|
||||
</Styleguide::Controls::Row>
|
||||
</Styleguide::Controls>
|
||||
</StyleguideExample>
|
|
@ -0,0 +1,86 @@
|
|||
import Component from "@glimmer/component";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { action } from "@ember/object";
|
||||
import ChatMessagesManager from "discourse/plugins/chat/discourse/lib/chat-messages-manager";
|
||||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
|
||||
export default class ChatStyleguideChatMessage extends Component {
|
||||
manager = new ChatMessagesManager(getOwner(this));
|
||||
|
||||
message = fabricators.message();
|
||||
|
||||
@action
|
||||
toggleDeleted() {
|
||||
if (this.message.deletedAt) {
|
||||
this.message.deletedAt = null;
|
||||
} else {
|
||||
this.message.deletedAt = moment();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
toggleBookmarked() {
|
||||
if (this.message.bookmark) {
|
||||
this.message.bookmark = null;
|
||||
} else {
|
||||
this.message.bookmark = fabricators.bookmark();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
toggleHighlighted() {
|
||||
this.message.highlighted = !this.message.highlighted;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleEdited() {
|
||||
this.message.edited = !this.message.edited;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleLastVisit() {
|
||||
this.message.newest = !this.message.newest;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleThread() {
|
||||
if (this.message.thread) {
|
||||
this.message.channel.threadingEnabled = false;
|
||||
this.message.thread = null;
|
||||
this.message.threadReplyCount = 0;
|
||||
} else {
|
||||
this.message.thread = fabricators.thread({
|
||||
channel: this.message.channel,
|
||||
});
|
||||
this.message.threadReplyCount = 1;
|
||||
this.message.channel.threadingEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
updateMessage(event) {
|
||||
this.message.message = event.target.value;
|
||||
this.message.cook();
|
||||
}
|
||||
|
||||
@action
|
||||
toggleReaction() {
|
||||
if (this.message.reactions?.length) {
|
||||
this.message.reactions = [];
|
||||
} else {
|
||||
this.message.reactions = [
|
||||
fabricators.reaction({ emoji: "heart" }),
|
||||
fabricators.reaction({ emoji: "rocket", reacted: true }),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
toggleUpload() {
|
||||
if (this.message.uploads?.length) {
|
||||
this.message.uploads = [];
|
||||
} else {
|
||||
this.message.uploads = [fabricators.upload(), fabricators.upload()];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<StyleguideExample @title="<ChatThreadOriginalMessage>">
|
||||
<Styleguide::Component>
|
||||
<Chat::Thread::OriginalMessage @message={{this.message}} />
|
||||
</Styleguide::Component>
|
||||
</StyleguideExample>
|
|
@ -0,0 +1,6 @@
|
|||
import Component from "@glimmer/component";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
export default class ChatStyleguideChatThreadOriginalMessage extends Component {
|
||||
message = fabricators.message();
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
Fabricators are used to create fake data for testing purposes.
|
||||
The following fabricators are available in lib folder to allow
|
||||
styleguide to use them, and eventually to generate dummy data
|
||||
in a placeholder component. It should not be used for any other case.
|
||||
*/
|
||||
|
||||
import ChatChannel, {
|
||||
CHANNEL_STATUSES,
|
||||
CHATABLE_TYPES,
|
||||
} from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
|
||||
import ChatThread from "discourse/plugins/chat/discourse/models/chat-thread";
|
||||
import ChatDirectMessage from "discourse/plugins/chat/discourse/models/chat-direct-message";
|
||||
import ChatMessageReaction from "discourse/plugins/chat/discourse/models/chat-message-reaction";
|
||||
import User from "discourse/models/user";
|
||||
import Bookmark from "discourse/models/bookmark";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
let sequence = 0;
|
||||
|
||||
function messageFabricator(args = {}) {
|
||||
const channel = args.channel || channelFabricator();
|
||||
|
||||
const message = ChatMessage.create(
|
||||
channel,
|
||||
Object.assign(
|
||||
{
|
||||
id: args.id || sequence++,
|
||||
user: args.user || userFabricator(),
|
||||
message:
|
||||
args.message ||
|
||||
"@discobot **abc**defghijklmnopqrstuvwxyz [discourse](discourse.org) :rocket: ",
|
||||
created_at: args.created_at || moment(),
|
||||
},
|
||||
args
|
||||
)
|
||||
);
|
||||
|
||||
const excerptLength = 50;
|
||||
const text = message.message.toString();
|
||||
if (text.length <= excerptLength) {
|
||||
message.excerpt = text;
|
||||
} else {
|
||||
message.excerpt = text.slice(0, excerptLength) + "...";
|
||||
}
|
||||
|
||||
message.cook();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
function channelFabricator(args = {}) {
|
||||
const id = args.id || sequence++;
|
||||
|
||||
return ChatChannel.create(
|
||||
Object.assign(
|
||||
{
|
||||
id,
|
||||
chatable_type:
|
||||
args.chatable?.type ||
|
||||
args.chatable_type ||
|
||||
CHATABLE_TYPES.categoryChannel,
|
||||
last_message_sent_at: args.last_message_sent_at,
|
||||
chatable_id: args.chatable?.id || args.chatable_id,
|
||||
title: args.title || "General",
|
||||
description: args.description,
|
||||
chatable: args.chatable || categoryFabricator(),
|
||||
status: CHANNEL_STATUSES.open,
|
||||
},
|
||||
args
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function categoryFabricator(args = {}) {
|
||||
return Category.create({
|
||||
id: args.id || sequence++,
|
||||
color: args.color || "D56353",
|
||||
read_restricted: false,
|
||||
name: args.name || "General",
|
||||
slug: args.slug || "general",
|
||||
});
|
||||
}
|
||||
|
||||
function directMessageFabricator(args = {}) {
|
||||
return ChatDirectMessage.create({
|
||||
id: args.id || sequence++,
|
||||
users: args.users || [userFabricator(), userFabricator()],
|
||||
});
|
||||
}
|
||||
|
||||
function directMessageChannelFabricator(args = {}) {
|
||||
const directMessage =
|
||||
args.chatable ||
|
||||
directMessageFabricator({
|
||||
id: args.chatable_id || sequence++,
|
||||
});
|
||||
|
||||
return channelFabricator(
|
||||
Object.assign(args, {
|
||||
chatable_type: CHATABLE_TYPES.directMessageChannel,
|
||||
chatable_id: directMessage.id,
|
||||
chatable: directMessage,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function userFabricator(args = {}) {
|
||||
return User.create({
|
||||
id: args.id || sequence++,
|
||||
username: args.username || "hawk",
|
||||
name: args.name,
|
||||
avatar_template: "/letter_avatar_proxy/v3/letter/t/41988e/{size}.png",
|
||||
});
|
||||
}
|
||||
|
||||
function bookmarkFabricator(args = {}) {
|
||||
return Bookmark.create({
|
||||
id: args.id || sequence++,
|
||||
});
|
||||
}
|
||||
|
||||
function threadFabricator(args = {}) {
|
||||
const channel = args.channel || channelFabricator();
|
||||
return ChatThread.create(channel, {
|
||||
id: args.id || sequence++,
|
||||
original_message: args.original_message || messageFabricator({ channel }),
|
||||
});
|
||||
}
|
||||
|
||||
function reactionFabricator(args = {}) {
|
||||
return ChatMessageReaction.create({
|
||||
count: args.count || 1,
|
||||
users: args.users || [userFabricator()],
|
||||
emoji: args.emoji || "heart",
|
||||
reacted: args.reacted || false,
|
||||
});
|
||||
}
|
||||
|
||||
function uploadFabricator() {
|
||||
return {
|
||||
extension: "jpeg",
|
||||
filesize: 126177,
|
||||
height: 800,
|
||||
human_filesize: "123 KB",
|
||||
id: 202,
|
||||
original_filename: "avatar.PNG.jpg",
|
||||
retain_hours: null,
|
||||
short_path: "/images/avatar.png",
|
||||
short_url: "upload://yoj8pf9DdIeHRRULyw7i57GAYdz.jpeg",
|
||||
thumbnail_height: 320,
|
||||
thumbnail_width: 690,
|
||||
url: "/images/avatar.png",
|
||||
width: 1920,
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
bookmark: bookmarkFabricator,
|
||||
user: userFabricator,
|
||||
channel: channelFabricator,
|
||||
directMessageChannel: directMessageChannelFabricator,
|
||||
message: messageFabricator,
|
||||
thread: threadFabricator,
|
||||
reaction: reactionFabricator,
|
||||
upload: uploadFabricator,
|
||||
category: categoryFabricator,
|
||||
directMessage: directMessageFabricator,
|
||||
};
|
|
@ -89,6 +89,7 @@ export default class ChatChannel {
|
|||
@tracked membershipsCount = 0;
|
||||
@tracked archive;
|
||||
@tracked tracking;
|
||||
@tracked threadingEnabled = false;
|
||||
|
||||
threadsManager = new ChatThreadsManager(getOwner(this));
|
||||
messagesManager = new ChatMessagesManager(getOwner(this));
|
||||
|
@ -114,7 +115,10 @@ export default class ChatChannel {
|
|||
this.autoJoinUsers = args.auto_join_users;
|
||||
this.allowChannelWideMentions = args.allow_channel_wide_mentions;
|
||||
this.chatable = this.isDirectMessageChannel
|
||||
? ChatDirectMessage.create(args)
|
||||
? ChatDirectMessage.create({
|
||||
id: args.chatable?.id,
|
||||
users: args.chatable?.users,
|
||||
})
|
||||
: Category.create(args.chatable);
|
||||
this.currentUserMembership = UserChatChannelMembership.create(
|
||||
args.current_user_membership
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import User from "discourse/models/user";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { CHATABLE_TYPES } from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||
|
||||
export default class ChatDirectMessage {
|
||||
static create(args = {}) {
|
||||
|
@ -9,9 +10,11 @@ export default class ChatDirectMessage {
|
|||
@tracked id;
|
||||
@tracked users = null;
|
||||
|
||||
type = CHATABLE_TYPES.drectMessageChannel;
|
||||
|
||||
constructor(args = {}) {
|
||||
this.id = args.chatable.id;
|
||||
this.users = this.#initUsers(args.chatable.users || []);
|
||||
this.id = args.id;
|
||||
this.users = this.#initUsers(args.users || []);
|
||||
}
|
||||
|
||||
#initUsers(users) {
|
||||
|
|
|
@ -12,9 +12,9 @@ export default class ChatMessageReaction {
|
|||
@tracked count = 0;
|
||||
@tracked reacted = false;
|
||||
@tracked users = [];
|
||||
@tracked emoji;
|
||||
|
||||
constructor(args = {}) {
|
||||
this.messageId = args.messageId;
|
||||
this.count = args.count;
|
||||
this.emoji = args.emoji;
|
||||
this.users = this.#initUsersModels(args.users);
|
||||
|
|
|
@ -7,7 +7,7 @@ import I18n from "I18n";
|
|||
import { generateCookFunction } from "discourse/lib/text";
|
||||
import simpleCategoryHashMentionTransform from "discourse/plugins/chat/discourse/lib/simple-category-hash-mention-transform";
|
||||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
|
||||
import { next } from "@ember/runloop";
|
||||
export default class ChatMessage {
|
||||
static cookFunction = null;
|
||||
|
||||
|
@ -38,9 +38,10 @@ export default class ChatMessage {
|
|||
@tracked expanded;
|
||||
@tracked bookmark;
|
||||
@tracked userFlagStatus;
|
||||
@tracked hidden;
|
||||
@tracked hidden = false;
|
||||
@tracked version = 0;
|
||||
@tracked edited;
|
||||
@tracked edited = false;
|
||||
@tracked editing = false;
|
||||
@tracked chatWebhookEvent = new TrackedObject();
|
||||
@tracked mentionWarning;
|
||||
@tracked availableFlags;
|
||||
|
@ -62,6 +63,7 @@ export default class ChatMessage {
|
|||
this.firstOfResults = args.firstOfResults;
|
||||
this.staged = args.staged;
|
||||
this.edited = args.edited;
|
||||
this.editing = args.editing;
|
||||
this.availableFlags = args.availableFlags || args.available_flags;
|
||||
this.hidden = args.hidden;
|
||||
this.threadReplyCount = args.threadReplyCount || args.thread_reply_count;
|
||||
|
@ -82,10 +84,7 @@ export default class ChatMessage {
|
|||
? ChatMessage.create(channel, args.in_reply_to || args.replyToMsg)
|
||||
: null);
|
||||
this.channel = channel;
|
||||
this.reactions = this.#initChatMessageReactionModel(
|
||||
args.id,
|
||||
args.reactions
|
||||
);
|
||||
this.reactions = this.#initChatMessageReactionModel(args.reactions);
|
||||
this.uploads = new TrackedArray(args.uploads || []);
|
||||
this.user = this.#initUserModel(args.user);
|
||||
this.bookmark = args.bookmark ? Bookmark.create(args.bookmark) : null;
|
||||
|
@ -138,6 +137,7 @@ export default class ChatMessage {
|
|||
}
|
||||
|
||||
cook() {
|
||||
next(() => {
|
||||
const site = getOwner(this).lookup("service:site");
|
||||
|
||||
const markdownOptions = {
|
||||
|
@ -165,6 +165,7 @@ export default class ChatMessage {
|
|||
this.cooked = ChatMessage.cookFunction(this.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get read() {
|
||||
|
@ -306,10 +307,8 @@ export default class ChatMessage {
|
|||
}
|
||||
}
|
||||
|
||||
#initChatMessageReactionModel(messageId, reactions = []) {
|
||||
return reactions.map((reaction) =>
|
||||
ChatMessageReaction.create(Object.assign({ messageId }, reaction))
|
||||
);
|
||||
#initChatMessageReactionModel(reactions = []) {
|
||||
return reactions.map((reaction) => ChatMessageReaction.create(reaction));
|
||||
}
|
||||
|
||||
#initUserModel(user) {
|
||||
|
|
|
@ -13,6 +13,10 @@ export const THREAD_STATUSES = {
|
|||
};
|
||||
|
||||
export default class ChatThread {
|
||||
static create(channel, args = {}) {
|
||||
return new ChatThread(channel, args);
|
||||
}
|
||||
|
||||
@tracked id;
|
||||
@tracked title;
|
||||
@tracked status;
|
||||
|
|
|
@ -4,7 +4,6 @@ import ChatComposer from "./chat-composer";
|
|||
|
||||
export default class ChatChannelComposer extends ChatComposer {
|
||||
@service chat;
|
||||
@service chatChannelThreadComposer;
|
||||
@service router;
|
||||
|
||||
@action
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<Styleguide::ChatMessage />
|
||||
<Styleguide::ChatComposer />
|
||||
<Styleguide::ChatThreadOriginalMessage />
|
||||
<Styleguide::ChatComposerMessageDetails />
|
|
@ -641,3 +641,8 @@ en:
|
|||
chat_notifications_with_unread:
|
||||
one: "Chat notifications - %{count} unread notification"
|
||||
other: "Chat notifications - %{count} unread notifications"
|
||||
|
||||
styleguide:
|
||||
sections:
|
||||
chat:
|
||||
title: Chat
|
||||
|
|
|
@ -49,13 +49,29 @@ Fabricator(:direct_message_channel, from: :chat_channel) do
|
|||
end
|
||||
end
|
||||
|
||||
Fabricator(:chat_message, class_name: "Chat::Message") do
|
||||
chat_channel
|
||||
user
|
||||
message "Beep boop"
|
||||
cooked { |attrs| Chat::Message.cook(attrs[:message]) }
|
||||
cooked_version Chat::Message::BAKED_VERSION
|
||||
in_reply_to nil
|
||||
Fabricator(:chat_message, class_name: "Chat::MessageCreator") do
|
||||
transient :chat_channel
|
||||
transient :user
|
||||
transient :message
|
||||
transient :in_reply_to
|
||||
transient :thread
|
||||
transient :upload_ids
|
||||
|
||||
initialize_with do |transients|
|
||||
user = transients[:user] || Fabricate(:user)
|
||||
channel =
|
||||
transients[:chat_channel] || transients[:thread]&.channel ||
|
||||
transients[:in_reply_to]&.chat_channel || Fabricate(:chat_channel)
|
||||
|
||||
resolved_class.create(
|
||||
chat_channel: channel,
|
||||
user: user,
|
||||
content: transients[:message] || Faker::Lorem.paragraph,
|
||||
thread_id: transients[:thread]&.id,
|
||||
in_reply_to_id: transients[:in_reply_to]&.id,
|
||||
upload_ids: transients[:upload_ids],
|
||||
).chat_message
|
||||
end
|
||||
end
|
||||
|
||||
Fabricator(:chat_mention, class_name: "Chat::Mention") do
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Chat | composer | shortcuts | thread", type: :system, js: true do
|
||||
fab!(:channel_1) { Fabricate(:chat_channel, threading_enabled: true) }
|
||||
fab!(:current_user) { Fabricate(:user) }
|
||||
fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel_1) }
|
||||
let(:chat_page) { PageObjects::Pages::Chat.new }
|
||||
let(:thread_page) { PageObjects::Pages::ChatThread.new }
|
||||
|
||||
before do
|
||||
SiteSetting.enable_experimental_chat_threaded_discussions = true
|
||||
chat_system_bootstrap
|
||||
channel_1.add(current_user)
|
||||
sign_in(current_user)
|
||||
end
|
||||
|
||||
describe "ArrowUp" do
|
||||
let(:thread_1) { message_1.reload.thread }
|
||||
|
||||
context "when there are editable messages" do
|
||||
let(:last_thread_message) { thread_1.replies.last }
|
||||
|
||||
before do
|
||||
thread_message_1 = Fabricate(:chat_message, user: current_user, in_reply_to: message_1)
|
||||
Fabricate(:chat_message, user: current_user, thread: thread_message_1.reload.thread)
|
||||
end
|
||||
|
||||
it "starts editing the last editable message" do
|
||||
chat_page.visit_thread(thread_1)
|
||||
|
||||
thread_page.composer.edit_last_message_shortcut
|
||||
|
||||
expect(thread_page.composer_message_details).to have_message(last_thread_message)
|
||||
expect(thread_page.composer.value).to eq(last_thread_message.message)
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are no editable messages" do
|
||||
before { Fabricate(:chat_message, in_reply_to: message_1) }
|
||||
|
||||
it "does nothing" do
|
||||
chat_page.visit_thread(thread_1)
|
||||
|
||||
thread_page.composer.edit_last_message_shortcut
|
||||
|
||||
expect(thread_page.composer_message_details).to have_no_message
|
||||
expect(thread_page.composer.value).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -45,9 +45,15 @@ RSpec.describe "Chat channel", type: :system, js: true do
|
|||
chat.visit_channel(channel_1)
|
||||
end
|
||||
|
||||
using_session(:tab_1) { channel.send_message("test_message") }
|
||||
using_session(:tab_1) do |session|
|
||||
channel.send_message("test_message")
|
||||
session.quit
|
||||
end
|
||||
|
||||
using_session(:tab_2) { expect(channel).to have_message(text: "test_message") }
|
||||
using_session(:tab_2) do |session|
|
||||
expect(channel).to have_message(text: "test_message")
|
||||
session.quit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -23,11 +23,12 @@ RSpec.describe "Edited message", type: :system, js: true do
|
|||
it "shows as edited for all users" do
|
||||
chat_page.visit_channel(channel_1)
|
||||
|
||||
using_session(:user_1) do
|
||||
using_session(:user_1) do |session|
|
||||
sign_in(editing_user)
|
||||
chat_page.visit_channel(channel_1)
|
||||
channel_page.edit_message(message_1, "a different message")
|
||||
expect(page).to have_content(I18n.t("js.chat.edited"))
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_content(I18n.t("js.chat.edited"))
|
||||
|
|
|
@ -35,7 +35,10 @@ RSpec.describe "Message notifications - mobile", type: :system, js: true, mobile
|
|||
Jobs.run_immediately!
|
||||
|
||||
visit("/chat")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
expect(page).to have_no_css(
|
||||
|
@ -62,7 +65,10 @@ RSpec.describe "Message notifications - mobile", type: :system, js: true, mobile
|
|||
Jobs.run_immediately!
|
||||
|
||||
visit("/chat")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".do-not-disturb-background")
|
||||
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
|
@ -77,7 +83,10 @@ RSpec.describe "Message notifications - mobile", type: :system, js: true, mobile
|
|||
Jobs.run_immediately!
|
||||
|
||||
visit("/chat")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
expect(page).to have_no_css(
|
||||
|
@ -92,7 +101,10 @@ RSpec.describe "Message notifications - mobile", type: :system, js: true, mobile
|
|||
Jobs.run_immediately!
|
||||
|
||||
visit("/chat")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "")
|
||||
expect(page).to have_css(
|
||||
|
@ -138,14 +150,20 @@ RSpec.describe "Message notifications - mobile", type: :system, js: true, mobile
|
|||
Jobs.run_immediately!
|
||||
|
||||
visit("/chat")
|
||||
using_session(:user_1) { create_message(channel: dm_channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: dm_channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "1")
|
||||
expect(page).to have_css(
|
||||
".chat-channel-row[data-chat-channel-id=\"#{dm_channel_1.id}\"] .chat-channel-unread-indicator",
|
||||
)
|
||||
|
||||
using_session(:user_1) { create_message(channel: dm_channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: dm_channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "2")
|
||||
end
|
||||
|
@ -162,7 +180,10 @@ RSpec.describe "Message notifications - mobile", type: :system, js: true, mobile
|
|||
".chat-channel-row:nth-child(2)[data-chat-channel-id=\"#{dm_channel_2.id}\"]",
|
||||
)
|
||||
|
||||
using_session(:user_1) { create_message(channel: dm_channel_2, creator: user_2) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: dm_channel_2, creator: user_2)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-channel-row:nth-child(1)[data-chat-channel-id=\"#{dm_channel_2.id}\"]",
|
||||
|
@ -190,14 +211,20 @@ RSpec.describe "Message notifications - mobile", type: :system, js: true, mobile
|
|||
Jobs.run_immediately!
|
||||
|
||||
visit("/chat")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "")
|
||||
expect(page).to have_css(
|
||||
".chat-channel-row[data-chat-channel-id=\"#{channel_1.id}\"] .chat-channel-unread-indicator",
|
||||
)
|
||||
|
||||
using_session(:user_1) { create_message(channel: dm_channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: dm_channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-channel-row[data-chat-channel-id=\"#{dm_channel_1.id}\"] .chat-channel-unread-indicator",
|
||||
|
|
|
@ -34,7 +34,10 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
|
|||
context "when a message is created" do
|
||||
it "doesn't show anything" do
|
||||
visit("/")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
expect(page).to have_no_css(".sidebar-row.channel-#{channel_1.id}")
|
||||
|
@ -59,7 +62,10 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
|
|||
Jobs.run_immediately!
|
||||
|
||||
visit("/")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".do-not-disturb-background")
|
||||
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
|
@ -72,7 +78,10 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
|
|||
context "when a message is created" do
|
||||
it "doesn't show anything" do
|
||||
visit("/")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
expect(page).to have_no_css(".sidebar-row.channel-#{channel_1.id} .unread")
|
||||
|
@ -91,7 +100,10 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
|
|||
context "when a message is created" do
|
||||
it "doesn't show any indicator on chat-header-icon" do
|
||||
visit("/")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
end
|
||||
|
@ -109,7 +121,10 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
|
|||
context "when a message is created" do
|
||||
it "doesn't show any indicator on chat-header-icon" do
|
||||
visit("/")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_no_css(
|
||||
".chat-header-icon .chat-channel-unread-indicator.urgent",
|
||||
|
@ -137,7 +152,10 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
|
|||
context "when a message is created" do
|
||||
it "correctly renders notifications" do
|
||||
visit("/")
|
||||
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "")
|
||||
expect(page).to have_css(".sidebar-row.channel-#{channel_1.id} .unread")
|
||||
|
@ -178,12 +196,18 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
|
|||
context "when a message is created" do
|
||||
it "correctly renders notifications" do
|
||||
visit("/")
|
||||
using_session(:user_1) { create_message(channel: dm_channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: dm_channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "1")
|
||||
expect(page).to have_css(".sidebar-row.channel-#{dm_channel_1.id} .icon.urgent")
|
||||
|
||||
using_session(:user_1) { create_message(channel: dm_channel_1, creator: user_1) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: dm_channel_1, creator: user_1)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "2")
|
||||
end
|
||||
|
@ -198,7 +222,10 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
|
|||
"#sidebar-section-content-chat-dms .sidebar-section-link-wrapper:nth-child(2) .channel-#{dm_channel_2.id}",
|
||||
)
|
||||
|
||||
using_session(:user_1) { create_message(channel: dm_channel_2, creator: user_2) }
|
||||
using_session(:user_1) do |session|
|
||||
create_message(channel: dm_channel_2, creator: user_2)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(
|
||||
"#sidebar-section-content-chat-dms .sidebar-section-link-wrapper:nth-child(1) .channel-#{dm_channel_2.id}",
|
||||
|
@ -237,12 +264,13 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
|
|||
session.quit
|
||||
end
|
||||
|
||||
using_session(:current_user) do
|
||||
using_session(:current_user) do |session|
|
||||
expect(page).to have_css(".sidebar-row.channel-#{dm_channel_1.id} .icon.urgent")
|
||||
expect(page).to have_css(
|
||||
".chat-header-icon .chat-channel-unread-indicator",
|
||||
text: "1",
|
||||
)
|
||||
session.quit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,8 +17,8 @@ module PageObjects
|
|||
visit("/chat")
|
||||
end
|
||||
|
||||
def visit_channel(channel, mobile: false)
|
||||
visit(channel.url + (mobile ? "?mobile_view=1" : ""))
|
||||
def visit_channel(channel)
|
||||
visit(channel.url)
|
||||
has_no_css?(".chat-channel--not-loaded-once")
|
||||
has_no_css?(".chat-skeleton")
|
||||
end
|
||||
|
|
|
@ -3,6 +3,15 @@
|
|||
module PageObjects
|
||||
module Pages
|
||||
class ChatThread < PageObjects::Pages::Base
|
||||
def composer
|
||||
@composer ||= PageObjects::Components::Chat::Composer.new(".chat-thread")
|
||||
end
|
||||
|
||||
def composer_message_details
|
||||
@composer_message_details ||=
|
||||
PageObjects::Components::Chat::ComposerMessageDetails.new(".chat-thread")
|
||||
end
|
||||
|
||||
def header
|
||||
find(".chat-thread__header")
|
||||
end
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Components
|
||||
module Chat
|
||||
class Composer < PageObjects::Components::Base
|
||||
attr_reader :context
|
||||
|
||||
SELECTOR = ".chat-composer__wrapper"
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def input
|
||||
find(context).find(SELECTOR).find(".chat-composer__input")
|
||||
end
|
||||
|
||||
def value
|
||||
input.value
|
||||
end
|
||||
|
||||
def reply_to_last_message_shortcut
|
||||
input.send_keys(%i[shift arrow_up])
|
||||
end
|
||||
|
||||
def edit_last_message_shortcut
|
||||
input.send_keys(%i[arrow_up])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Components
|
||||
module Chat
|
||||
class ComposerMessageDetails < PageObjects::Components::Base
|
||||
attr_reader :context
|
||||
|
||||
SELECTOR = ".chat-composer-message-details"
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def has_message?(message)
|
||||
find(context).find(SELECTOR + "[data-id=\"#{message.id}\"]")
|
||||
end
|
||||
|
||||
def has_no_message?
|
||||
find(context).has_no_css?(SELECTOR)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -87,7 +87,7 @@ RSpec.describe "Reply to message - channel - drawer", type: :system, js: true do
|
|||
|
||||
expect(page).to have_selector(
|
||||
".chat-channel .chat-reply__excerpt",
|
||||
text: original_message.message,
|
||||
text: original_message.excerpt,
|
||||
)
|
||||
|
||||
channel_page.fill_composer("reply to message")
|
||||
|
|
|
@ -91,7 +91,7 @@ RSpec.describe "Reply to message - channel - full page", type: :system, js: true
|
|||
|
||||
expect(page).to have_selector(
|
||||
".chat-channel .chat-reply__excerpt",
|
||||
text: original_message.message,
|
||||
text: original_message.excerpt,
|
||||
)
|
||||
|
||||
channel_page.fill_composer("reply to message")
|
||||
|
|
|
@ -97,7 +97,7 @@ RSpec.describe "Reply to message - channel - mobile", type: :system, js: true, m
|
|||
|
||||
expect(page).to have_selector(
|
||||
".chat-channel .chat-reply__excerpt",
|
||||
text: original_message.message,
|
||||
text: original_message.excerpt,
|
||||
)
|
||||
|
||||
channel_page.fill_composer("reply to message")
|
||||
|
|
|
@ -161,15 +161,17 @@ describe "Single thread in side panel", type: :system, js: true do
|
|||
expect(thread_page).to have_message(thread_id: thread.id, text: "the other user message")
|
||||
end
|
||||
|
||||
using_session(:tab_1) do
|
||||
using_session(:tab_1) do |session|
|
||||
expect(side_panel).to have_open_thread(thread)
|
||||
expect(thread_page).to have_message(thread_id: thread.id, text: "the other user message")
|
||||
thread_page.send_message("this is a test message")
|
||||
expect(thread_page).to have_message(thread_id: thread.id, text: "this is a test message")
|
||||
session.quit
|
||||
end
|
||||
|
||||
using_session(:tab_2) do
|
||||
using_session(:tab_2) do |session|
|
||||
expect(thread_page).to have_message(thread_id: thread.id, text: "this is a test message")
|
||||
session.quit
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,12 +22,13 @@ RSpec.describe "Unfollow dm channel", type: :system, js: true do
|
|||
|
||||
expect(page).to have_no_css(".channel-#{dm_channel_1.id}")
|
||||
|
||||
using_session(:user_1) do
|
||||
using_session(:user_1) do |session|
|
||||
text = "this is fine"
|
||||
sign_in(other_user)
|
||||
chat_page.visit_channel(dm_channel_1)
|
||||
chat_channel_page.send_message(text)
|
||||
expect(chat_channel_page).to have_message(text: text)
|
||||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".channel-#{dm_channel_1.id} .urgent")
|
||||
|
|
|
@ -198,13 +198,14 @@ RSpec.describe "User menu notifications | sidebar", type: :system, js: true do
|
|||
channel.send_message("this is fine @#{other_user.username}")
|
||||
find(".invite-link", wait: 5).click
|
||||
|
||||
using_session(:user_1) do
|
||||
using_session(:user_1) do |session|
|
||||
sign_in(other_user)
|
||||
visit("/")
|
||||
find(".header-dropdown-toggle.current-user").click
|
||||
|
||||
expect(find("#user-menu-button-chat-notifications")).to have_content(1)
|
||||
expect(find("#quick-access-all-notifications")).to have_css(".chat-invitation.unread")
|
||||
session.quit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import { render } from "@ember/test-helpers";
|
||||
|
@ -13,7 +13,7 @@ module(
|
|||
test("channel title is escaped in instructions correctly", async function (assert) {
|
||||
this.set(
|
||||
"channel",
|
||||
fabricators.chatChannel({
|
||||
fabricators.channel({
|
||||
title: `<script>someeviltitle</script>`,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import I18n from "I18n";
|
||||
|
@ -10,7 +10,7 @@ module("Discourse Chat | Component | chat-channel-card", function (hooks) {
|
|||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
this.channel.description =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import { render } from "@ember/test-helpers";
|
||||
|
@ -13,7 +13,7 @@ module(
|
|||
test("channel title is escaped in instructions correctly", async function (assert) {
|
||||
this.set(
|
||||
"channel",
|
||||
fabricators.chatChannel({
|
||||
fabricators.channel({
|
||||
title: `<script>someeviltitle</script>`,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@ import hbs from "htmlbars-inline-precompile";
|
|||
import pretender from "discourse/tests/helpers/create-pretender";
|
||||
import I18n from "I18n";
|
||||
import { module, test } from "qunit";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
module("Discourse Chat | Component | chat-channel-leave-btn", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
@ -13,7 +13,7 @@ module("Discourse Chat | Component | chat-channel-leave-btn", function (hooks) {
|
|||
test("accepts an optional onLeaveChannel callback", async function (assert) {
|
||||
this.foo = 1;
|
||||
this.onLeaveChannel = () => (this.foo = 2);
|
||||
this.channel = fabricators.directMessageChatChannel({ users: [{ id: 1 }] });
|
||||
this.channel = fabricators.directMessageChannel();
|
||||
|
||||
await render(
|
||||
hbs`<ChatChannelLeaveBtn @channel={{this.channel}} @onLeaveChannel={{this.onLeaveChannel}} />`
|
||||
|
@ -29,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) {
|
||||
this.channel = fabricators.directMessageChatChannel();
|
||||
this.channel = fabricators.directMessageChannel();
|
||||
|
||||
await render(hbs`<ChatChannelLeaveBtn @channel={{this.channel}} />`);
|
||||
|
||||
|
@ -38,7 +38,7 @@ module("Discourse Chat | Component | chat-channel-leave-btn", function (hooks) {
|
|||
});
|
||||
|
||||
test("has a specific title for message channel", async function (assert) {
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
|
||||
await render(hbs`<ChatChannelLeaveBtn @channel={{this.channel}} />`);
|
||||
|
||||
|
@ -48,7 +48,7 @@ module("Discourse Chat | Component | chat-channel-leave-btn", function (hooks) {
|
|||
|
||||
test("is not visible on mobile", async function (assert) {
|
||||
this.site.mobileView = true;
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
|
||||
await render(hbs`<ChatChannelLeaveBtn @channel={{this.channel}} />`);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { exists } from "discourse/tests/helpers/qunit-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
|
||||
|
@ -10,7 +10,7 @@ module("Discourse Chat | Component | chat-channel-metadata", function (hooks) {
|
|||
|
||||
test("displays last message sent at", async function (assert) {
|
||||
let lastMessageSentAt = moment().subtract(1, "day").format();
|
||||
this.channel = fabricators.directMessageChatChannel({
|
||||
this.channel = fabricators.directMessageChannel({
|
||||
last_message_sent_at: lastMessageSentAt,
|
||||
});
|
||||
|
||||
|
@ -28,7 +28,7 @@ module("Discourse Chat | Component | chat-channel-metadata", function (hooks) {
|
|||
});
|
||||
|
||||
test("unreadIndicator", async function (assert) {
|
||||
this.channel = fabricators.directMessageChatChannel();
|
||||
this.channel = fabricators.directMessageChannel();
|
||||
this.channel.tracking.unreadCount = 1;
|
||||
|
||||
this.unreadIndicator = true;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
|||
import hbs from "htmlbars-inline-precompile";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
module(
|
||||
"Discourse Chat | Component | chat-channel-preview-card",
|
||||
|
@ -11,10 +11,7 @@ module(
|
|||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.set(
|
||||
"channel",
|
||||
fabricators.chatChannel({ chatable_type: "Category" })
|
||||
);
|
||||
this.set("channel", fabricators.channel({ chatable_type: "Category" }));
|
||||
|
||||
this.channel.description = "Important stuff is announced here.";
|
||||
this.channel.title = "announcements";
|
||||
|
|
|
@ -2,14 +2,14 @@ import { module, test } from "qunit";
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
module("Discourse Chat | Component | chat-channel-row", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.categoryChatChannel = fabricators.chatChannel();
|
||||
this.directMessageChatChannel = fabricators.directMessageChatChannel();
|
||||
this.categoryChatChannel = fabricators.channel();
|
||||
this.directMessageChannel = fabricators.directMessageChannel();
|
||||
});
|
||||
|
||||
test("links to correct channel", async function (assert) {
|
||||
|
@ -47,11 +47,14 @@ module("Discourse Chat | Component | chat-channel-row", function (hooks) {
|
|||
});
|
||||
|
||||
test("renders correct channel metadata", async function (assert) {
|
||||
this.categoryChatChannel.lastMessageSentAt = moment().toISOString();
|
||||
await render(hbs`<ChatChannelRow @channel={{this.categoryChatChannel}} />`);
|
||||
|
||||
assert
|
||||
.dom(".chat-channel-metadata")
|
||||
.hasText(moment(this.categoryChatChannel.lastMessageSentAt).format("l"));
|
||||
.hasText(
|
||||
moment(this.categoryChatChannel.lastMessageSentAt).format("h:mm A")
|
||||
);
|
||||
});
|
||||
|
||||
test("renders membership toggling button when necessary", async function (assert) {
|
||||
|
@ -145,11 +148,14 @@ module("Discourse Chat | Component | chat-channel-row", function (hooks) {
|
|||
});
|
||||
|
||||
test("user status with direct message channel", async function (assert) {
|
||||
this.directMessageChannel.chatable = fabricators.directMessage({
|
||||
users: [fabricators.user()],
|
||||
});
|
||||
const status = { description: "Off to dentist", emoji: "tooth" };
|
||||
this.directMessageChatChannel.chatable.users[0].status = status;
|
||||
this.directMessageChannel.chatable.users[0].status = status;
|
||||
|
||||
await render(
|
||||
hbs`<ChatChannelRow @channel={{this.directMessageChatChannel}} />`
|
||||
hbs`<ChatChannelRow @channel={{this.directMessageChannel}} />`
|
||||
);
|
||||
|
||||
assert.dom(".user-status-message").exists();
|
||||
|
@ -157,9 +163,9 @@ module("Discourse Chat | Component | chat-channel-row", function (hooks) {
|
|||
|
||||
test("user status with direct message channel and multiple users", async function (assert) {
|
||||
const status = { description: "Off to dentist", emoji: "tooth" };
|
||||
this.directMessageChatChannel.chatable.users[0].status = status;
|
||||
this.directMessageChannel.chatable.users[0].status = status;
|
||||
|
||||
this.directMessageChatChannel.chatable.users.push({
|
||||
this.directMessageChannel.chatable.users.push({
|
||||
id: 2,
|
||||
username: "bill",
|
||||
name: null,
|
||||
|
@ -167,7 +173,7 @@ module("Discourse Chat | Component | chat-channel-row", function (hooks) {
|
|||
});
|
||||
|
||||
await render(
|
||||
hbs`<ChatChannelRow @channel={{this.directMessageChatChannel}} />`
|
||||
hbs`<ChatChannelRow @channel={{this.directMessageChannel}} />`
|
||||
);
|
||||
|
||||
assert.dom(".user-status-message").doesNotExist();
|
||||
|
|
|
@ -3,7 +3,7 @@ import hbs from "htmlbars-inline-precompile";
|
|||
import I18n from "I18n";
|
||||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import {
|
||||
CHANNEL_STATUSES,
|
||||
channelStatusIcon,
|
||||
|
@ -13,7 +13,7 @@ module("Discourse Chat | Component | chat-channel-status", function (hooks) {
|
|||
setupRenderingTest(hooks);
|
||||
|
||||
test("renders nothing when channel is opened", async function (assert) {
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
|
||||
await render(hbs`<ChatChannelStatus @channel={{this.channel}} />`);
|
||||
|
||||
|
@ -21,7 +21,7 @@ module("Discourse Chat | Component | chat-channel-status", function (hooks) {
|
|||
});
|
||||
|
||||
test("defaults to long format", async function (assert) {
|
||||
this.channel = fabricators.chatChannel({ status: CHANNEL_STATUSES.closed });
|
||||
this.channel = fabricators.channel({ status: CHANNEL_STATUSES.closed });
|
||||
|
||||
await render(hbs`<ChatChannelStatus @channel={{this.channel}} />`);
|
||||
|
||||
|
@ -31,7 +31,7 @@ module("Discourse Chat | Component | chat-channel-status", function (hooks) {
|
|||
});
|
||||
|
||||
test("accepts a format argument", async function (assert) {
|
||||
this.channel = fabricators.chatChannel({
|
||||
this.channel = fabricators.channel({
|
||||
status: CHANNEL_STATUSES.archived,
|
||||
});
|
||||
|
||||
|
@ -45,7 +45,7 @@ module("Discourse Chat | Component | chat-channel-status", function (hooks) {
|
|||
});
|
||||
|
||||
test("renders the correct icon", async function (assert) {
|
||||
this.channel = fabricators.chatChannel({
|
||||
this.channel = fabricators.channel({
|
||||
status: CHANNEL_STATUSES.archived,
|
||||
});
|
||||
|
||||
|
@ -56,7 +56,7 @@ module("Discourse Chat | Component | chat-channel-status", function (hooks) {
|
|||
|
||||
test("renders archive status", async function (assert) {
|
||||
this.currentUser.admin = true;
|
||||
this.channel = fabricators.chatChannel({
|
||||
this.channel = fabricators.channel({
|
||||
status: CHANNEL_STATUSES.archived,
|
||||
archive_failed: true,
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { CHATABLE_TYPES } from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
|
@ -10,7 +10,7 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||
setupRenderingTest(hooks);
|
||||
|
||||
test("category channel", async function (assert) {
|
||||
this.channel = fabricators.chatChannel({
|
||||
this.channel = fabricators.channel({
|
||||
chatable_type: CHATABLE_TYPES.categoryChannel,
|
||||
});
|
||||
|
||||
|
@ -27,7 +27,7 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||
});
|
||||
|
||||
test("category channel - escapes title", async function (assert) {
|
||||
this.channel = fabricators.chatChannel({
|
||||
this.channel = fabricators.channel({
|
||||
chatable_type: CHATABLE_TYPES.categoryChannel,
|
||||
title: "<div class='xss'>evil</div>",
|
||||
});
|
||||
|
@ -38,7 +38,7 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||
});
|
||||
|
||||
test("category channel - read restricted", async function (assert) {
|
||||
this.channel = fabricators.chatChannel({
|
||||
this.channel = fabricators.channel({
|
||||
chatable_type: CHATABLE_TYPES.categoryChannel,
|
||||
chatable: { read_restricted: true },
|
||||
});
|
||||
|
@ -49,7 +49,7 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||
});
|
||||
|
||||
test("category channel - not read restricted", async function (assert) {
|
||||
this.channel = fabricators.chatChannel({
|
||||
this.channel = fabricators.channel({
|
||||
chatable_type: CHATABLE_TYPES.categoryChannel,
|
||||
chatable: { read_restricted: false },
|
||||
});
|
||||
|
@ -60,7 +60,11 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||
});
|
||||
|
||||
test("direct message channel - one user", async function (assert) {
|
||||
this.channel = fabricators.directMessageChatChannel();
|
||||
this.channel = fabricators.directMessageChannel({
|
||||
chatable: fabricators.directMessage({
|
||||
users: [fabricators.user()],
|
||||
}),
|
||||
});
|
||||
|
||||
await render(hbs`<ChatChannelTitle @channel={{this.channel}} />`);
|
||||
|
||||
|
@ -77,7 +81,7 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
|
|||
});
|
||||
|
||||
test("direct message channel - multiple users", async function (assert) {
|
||||
const channel = fabricators.directMessageChatChannel();
|
||||
const channel = fabricators.directMessageChannel();
|
||||
|
||||
channel.chatable.users.push({
|
||||
id: 2,
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
|
||||
module(
|
||||
"Discourse Chat | Component | chat-composer-message-details",
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("data-id attribute", async function (assert) {
|
||||
this.message = fabricators.message();
|
||||
|
||||
await render(
|
||||
hbs`<ChatComposerMessageDetails @message={{this.message}} />`
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(".chat-composer-message-details")
|
||||
.hasAttribute("data-id", this.message.id.toString());
|
||||
});
|
||||
|
||||
test("editing a message has the pencil icon", async function (assert) {
|
||||
this.message = fabricators.message({ editing: true });
|
||||
|
||||
await render(
|
||||
hbs`<ChatComposerMessageDetails @message={{this.message}} />`
|
||||
);
|
||||
|
||||
assert.dom(".chat-composer-message-details .d-icon-pencil-alt").exists();
|
||||
});
|
||||
|
||||
test("replying to a message has the reply icon", async function (assert) {
|
||||
const firstMessage = fabricators.message();
|
||||
this.message = fabricators.message({ inReplyTo: firstMessage });
|
||||
|
||||
await render(
|
||||
hbs`<ChatComposerMessageDetails @message={{this.message}} />`
|
||||
);
|
||||
|
||||
assert.dom(".chat-composer-message-details .d-icon-reply").exists();
|
||||
});
|
||||
|
||||
test("displays user avatar", async function (assert) {
|
||||
this.message = fabricators.message();
|
||||
|
||||
await render(
|
||||
hbs`<ChatComposerMessageDetails @message={{this.message}} />`
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(".chat-composer-message-details .chat-user-avatar .avatar")
|
||||
.hasAttribute("title", this.message.user.username);
|
||||
});
|
||||
|
||||
test("displays message excerpt", async function (assert) {
|
||||
this.message = fabricators.message();
|
||||
|
||||
await render(
|
||||
hbs`<ChatComposerMessageDetails @message={{this.message}} />`
|
||||
);
|
||||
|
||||
assert.dom(".chat-reply__excerpt").hasText(this.message.excerpt);
|
||||
});
|
||||
|
||||
test("displays user’s username", async function (assert) {
|
||||
this.message = fabricators.message();
|
||||
|
||||
await render(
|
||||
hbs`<ChatComposerMessageDetails @message={{this.message}} />`
|
||||
);
|
||||
|
||||
assert.dom(".chat-reply__username").hasText(this.message.user.username);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -4,13 +4,13 @@ import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
|||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
module("Discourse Chat | Component | chat-message-avatar", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("chat_webhook_event", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
chat_webhook_event: { emoji: ":heart:" },
|
||||
});
|
||||
|
||||
|
@ -20,7 +20,7 @@ module("Discourse Chat | Component | chat-message-avatar", function (hooks) {
|
|||
});
|
||||
|
||||
test("user", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: { username: "discobot" },
|
||||
});
|
||||
|
||||
|
|
|
@ -6,13 +6,13 @@ import I18n from "I18n";
|
|||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("chat_webhook_event", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
chat_webhook_event: { username: "discobot" },
|
||||
});
|
||||
|
||||
|
@ -29,7 +29,7 @@ module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
|||
});
|
||||
|
||||
test("user", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: { username: "discobot" },
|
||||
});
|
||||
|
||||
|
@ -42,7 +42,7 @@ module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
|||
});
|
||||
|
||||
test("date", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: { username: "discobot" },
|
||||
created_at: moment(),
|
||||
});
|
||||
|
@ -53,7 +53,7 @@ module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
|||
});
|
||||
|
||||
test("bookmark (with reminder)", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: { username: "discobot" },
|
||||
bookmark: Bookmark.create({
|
||||
reminder_at: moment(),
|
||||
|
@ -69,7 +69,7 @@ module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
|||
});
|
||||
|
||||
test("bookmark (no reminder)", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: { username: "discobot" },
|
||||
bookmark: Bookmark.create({
|
||||
name: "some name",
|
||||
|
@ -83,7 +83,7 @@ module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
|||
|
||||
test("user status", async function (assert) {
|
||||
const status = { description: "off to dentist", emoji: "tooth" };
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: { status },
|
||||
});
|
||||
|
||||
|
@ -93,7 +93,7 @@ module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
|||
});
|
||||
|
||||
test("reviewable", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: { username: "discobot" },
|
||||
user_flag_status: 0,
|
||||
});
|
||||
|
@ -105,7 +105,7 @@ module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
|||
I18n.t("chat.you_flagged")
|
||||
);
|
||||
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: { username: "discobot" },
|
||||
reviewable_id: 1,
|
||||
});
|
||||
|
@ -119,7 +119,7 @@ module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
|||
});
|
||||
|
||||
test("with username classes", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: {
|
||||
username: "discobot",
|
||||
admin: true,
|
||||
|
@ -139,7 +139,7 @@ module("Discourse Chat | Component | chat-message-info", function (hooks) {
|
|||
});
|
||||
|
||||
test("without username classes", async function (assert) {
|
||||
this.message = ChatMessage.create(fabricators.chatChannel(), {
|
||||
this.message = ChatMessage.create(fabricators.channel(), {
|
||||
user: { username: "discobot" },
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import { render } from "@ember/test-helpers";
|
||||
|
@ -13,7 +13,7 @@ module(
|
|||
test("channel title is escaped in instructions correctly", async function (assert) {
|
||||
this.set(
|
||||
"channel",
|
||||
fabricators.chatChannel({ title: "<script>someeviltitle</script>" })
|
||||
fabricators.channel({ title: "<script>someeviltitle</script>" })
|
||||
);
|
||||
this.set("chat", { publicChannels: [this.channel] });
|
||||
this.set("selectedMessageIds", [1]);
|
||||
|
|
|
@ -55,7 +55,6 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
|
|||
const template = hbs`
|
||||
<ChatMessage
|
||||
@message={{this.message}}
|
||||
@channel={{this.channel}}
|
||||
@messageDidEnterViewport={{this.messageDidEnterViewport}}
|
||||
@messageDidLeaveViewport={{this.messageDidLeaveViewport}}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import {
|
||||
|
@ -87,7 +87,7 @@ module(
|
|||
});
|
||||
|
||||
test("displays indicator when 2 or 3 users are replying", async function (assert) {
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
|
||||
await render(
|
||||
hbs`<ChatReplyingIndicator @presenceChannelName="/chat-reply/1" />`
|
||||
|
@ -102,7 +102,7 @@ module(
|
|||
});
|
||||
|
||||
test("displays indicator when 3 users are replying", async function (assert) {
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
|
||||
await render(
|
||||
hbs`<ChatReplyingIndicator @presenceChannelName="/chat-reply/1" />`
|
||||
|
@ -118,7 +118,7 @@ module(
|
|||
});
|
||||
|
||||
test("displays indicator when more than 3 users are replying", async function (assert) {
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
|
||||
await render(
|
||||
hbs`<ChatReplyingIndicator @presenceChannelName="/chat-reply/1" />`
|
||||
|
@ -135,7 +135,7 @@ module(
|
|||
});
|
||||
|
||||
test("filters current user from list of repliers", async function (assert) {
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
|
||||
await render(
|
||||
hbs`<ChatReplyingIndicator @presenceChannelName="/chat-reply/1" />`
|
||||
|
|
|
@ -3,7 +3,7 @@ import hbs from "htmlbars-inline-precompile";
|
|||
import I18n from "I18n";
|
||||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
module(
|
||||
"Discourse Chat | Component | chat-retention-reminder-text",
|
||||
|
@ -11,7 +11,7 @@ module(
|
|||
setupRenderingTest(hooks);
|
||||
|
||||
test("when setting is set on 0", async function (assert) {
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
this.siteSettings.chat_channel_retention_days = 0;
|
||||
|
||||
await render(
|
||||
|
@ -25,7 +25,7 @@ module(
|
|||
|
||||
test("when channel is a public channel", async function (assert) {
|
||||
const count = 10;
|
||||
this.channel = fabricators.chatChannel();
|
||||
this.channel = fabricators.channel();
|
||||
this.siteSettings.chat_channel_retention_days = count;
|
||||
|
||||
await render(
|
||||
|
@ -39,7 +39,7 @@ module(
|
|||
|
||||
test("when channel is a DM channel", async function (assert) {
|
||||
const count = 10;
|
||||
this.channel = fabricators.directMessageChatChannel();
|
||||
this.channel = fabricators.directMessageChannel();
|
||||
this.siteSettings.chat_dm_retention_days = count;
|
||||
|
||||
await render(
|
||||
|
|
|
@ -4,7 +4,7 @@ import hbs from "htmlbars-inline-precompile";
|
|||
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||
import { Promise } from "rsvp";
|
||||
import fabricators from "../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
import { module, test } from "qunit";
|
||||
|
||||
function mockChat(context, options = {}) {
|
||||
|
@ -15,7 +15,7 @@ function mockChat(context, options = {}) {
|
|||
});
|
||||
};
|
||||
mock.getDmChannelForUsernames = () => {
|
||||
return Promise.resolve({ chat_channel: fabricators.chatChannel() });
|
||||
return Promise.resolve({ chat_channel: fabricators.channel() });
|
||||
};
|
||||
return mock;
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ module("Discourse Chat | Component | direct-message-creator", function (hooks) {
|
|||
await fillIn(".filter-usernames", "hawk");
|
||||
assert.strictEqual(query(".filter-usernames").value, "hawk");
|
||||
|
||||
this.set("channel", fabricators.chatChannel());
|
||||
this.set("channel", fabricators.channel());
|
||||
this.set("channel", ChatChannel.createDirectMessageChannelDraft());
|
||||
|
||||
assert.strictEqual(query(".filter-usernames").value, "");
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import { cloneJSON } from "discourse-common/lib/object";
|
||||
|
||||
// heavily inspired by https://github.com/travelperk/fabricator
|
||||
export function Fabricator(Model, attributes = {}) {
|
||||
return (opts) => fabricate(Model, attributes, opts);
|
||||
}
|
||||
|
||||
function fabricate(Model, attributes, opts = {}) {
|
||||
if (typeof attributes === "function") {
|
||||
return attributes();
|
||||
}
|
||||
|
||||
const extendedModel = cloneJSON({ ...attributes, ...opts });
|
||||
const props = {};
|
||||
|
||||
for (const [key, value] of Object.entries(extendedModel)) {
|
||||
props[key] = typeof value === "function" ? value() : value;
|
||||
}
|
||||
|
||||
return Model.create(props);
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
import ChatChannel, {
|
||||
CHATABLE_TYPES,
|
||||
} from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||
import EmberObject from "@ember/object";
|
||||
import { Fabricator } from "./fabricator";
|
||||
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
|
||||
|
||||
const userFabricator = Fabricator(EmberObject, {
|
||||
id: 1,
|
||||
username: "hawk",
|
||||
name: null,
|
||||
avatar_template: "/letter_avatar_proxy/v3/letter/t/41988e/{size}.png",
|
||||
});
|
||||
|
||||
const categoryChatableFabricator = Fabricator(EmberObject, {
|
||||
id: 1,
|
||||
color: "D56353",
|
||||
read_restricted: false,
|
||||
name: "My category",
|
||||
});
|
||||
|
||||
const directChannelChatableFabricator = Fabricator(EmberObject, {
|
||||
users: [userFabricator({ id: 1, username: "bob" })],
|
||||
});
|
||||
|
||||
export default {
|
||||
chatChannel: Fabricator(ChatChannel, {
|
||||
id: 1,
|
||||
chatable_type: CHATABLE_TYPES.categoryChannel,
|
||||
status: "open",
|
||||
title: "My category title",
|
||||
name: "My category name",
|
||||
chatable: categoryChatableFabricator(),
|
||||
last_message_sent_at: "2021-11-08T21:26:05.710Z",
|
||||
allow_channel_wide_mentions: true,
|
||||
message_bus_last_ids: {
|
||||
new_mentions: 0,
|
||||
new_messages: 0,
|
||||
},
|
||||
}),
|
||||
|
||||
chatChannelMessage: Fabricator(ChatMessage, {
|
||||
id: 1,
|
||||
chat_channel_id: 1,
|
||||
user_id: 1,
|
||||
cooked: "This is a test message",
|
||||
}),
|
||||
|
||||
directMessageChatChannel: Fabricator(ChatChannel, {
|
||||
id: 1,
|
||||
chatable_type: CHATABLE_TYPES.directMessageChannel,
|
||||
status: "open",
|
||||
chatable: directChannelChatableFabricator(),
|
||||
last_message_sent_at: "2021-11-08T21:26:05.710Z",
|
||||
message_bus_last_ids: {
|
||||
new_mentions: 0,
|
||||
new_messages: 0,
|
||||
},
|
||||
}),
|
||||
};
|
|
@ -3,21 +3,20 @@ import hbs from "htmlbars-inline-precompile";
|
|||
import { render } from "@ember/test-helpers";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import fabricators from "../../helpers/fabricators";
|
||||
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
module("Discourse Chat | Unit | Helpers | format-chat-date", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("link to chat message", async function (assert) {
|
||||
const channel = fabricators.chatChannel();
|
||||
this.message = ChatMessage.create(channel, {
|
||||
id: 1,
|
||||
chat_channel_id: channel.id,
|
||||
});
|
||||
const channel = fabricators.channel();
|
||||
this.message = fabricators.message({ channel });
|
||||
|
||||
await render(hbs`{{format-chat-date this.message}}`);
|
||||
|
||||
assert.equal(query(".chat-time").getAttribute("href"), "/chat/c/-/1/1");
|
||||
assert.equal(
|
||||
query(".chat-time").getAttribute("href"),
|
||||
`/chat/c/-/${channel.id}/${this.message.id}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { test } from "qunit";
|
||||
import { set } from "@ember/object";
|
||||
import fabricators from "../../helpers/fabricators";
|
||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
acceptance("Discourse Chat | Unit | Service | chat-guardian", function (needs) {
|
||||
needs.hooks.beforeEach(function () {
|
||||
|
@ -69,7 +69,7 @@ acceptance("Discourse Chat | Unit | Service | chat-guardian", function (needs) {
|
|||
});
|
||||
|
||||
test("#canArchiveChannel", async function (assert) {
|
||||
const channel = fabricators.chatChannel();
|
||||
const channel = fabricators.channel();
|
||||
|
||||
set(this.currentUser, "has_chat_enabled", true);
|
||||
set(this.currentUser, "admin", true);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<div class="component">
|
||||
{{yield}}
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
import Component from "@glimmer/component";
|
||||
|
||||
export default class StyleguideComponent extends Component {}
|
|
@ -0,0 +1,5 @@
|
|||
<table class="component-properties">
|
||||
<tbody>
|
||||
{{yield}}
|
||||
</tbody>
|
||||
</table>
|
3
plugins/styleguide/assets/javascripts/discourse/components/styleguide/controls.js
vendored
Normal file
3
plugins/styleguide/assets/javascripts/discourse/components/styleguide/controls.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Component from "@glimmer/component";
|
||||
|
||||
export default class StyleguideControls extends Component {}
|
|
@ -0,0 +1,6 @@
|
|||
<tr class="component-properties__row">
|
||||
<td class="component-properties__cell">{{@name}}</td>
|
||||
<td class="component-properties__cell">
|
||||
{{yield}}
|
||||
</td>
|
||||
</tr>
|
|
@ -0,0 +1 @@
|
|||
<DToggleSwitch @state={{@enabled}} {{on "click" @action}} />
|
|
@ -0,0 +1,3 @@
|
|||
import Component from "@glimmer/component";
|
||||
|
||||
export default class StyleguideControlsToggle extends Component {}
|
|
@ -0,0 +1 @@
|
|||
<DButton @action={{this.toggle}} class="toggle-color-mode">Toggle color</DButton>
|
|
@ -0,0 +1,45 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
const DARK = "dark";
|
||||
const LIGHT = "light";
|
||||
|
||||
function colorSchemeOverride(type) {
|
||||
const lightScheme = document.querySelector("link.light-scheme");
|
||||
const darkScheme = document.querySelector("link.dark-scheme");
|
||||
|
||||
if (!lightScheme || !darkScheme) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case DARK:
|
||||
lightScheme.media = "none";
|
||||
darkScheme.media = "all";
|
||||
break;
|
||||
case LIGHT:
|
||||
lightScheme.media = "all";
|
||||
darkScheme.media = "none";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export default class ToggleColorMode extends Component {
|
||||
@service keyValueStore;
|
||||
|
||||
@tracked colorSchemeOverride = this.default;
|
||||
|
||||
get default() {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? DARK
|
||||
: LIGHT;
|
||||
}
|
||||
|
||||
@action
|
||||
toggle() {
|
||||
this.colorSchemeOverride = this.colorSchemeOverride === DARK ? LIGHT : DARK;
|
||||
colorSchemeOverride(this.colorSchemeOverride);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
<section class="styleguide">
|
||||
<section class="styleguide-menu">
|
||||
<ToggleColorMode />
|
||||
{{#each this.categories as |c|}}
|
||||
<ul>
|
||||
<li class="styleguide-heading">{{i18n
|
||||
|
|
|
@ -73,6 +73,36 @@
|
|||
|
||||
.rendered {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.component {
|
||||
padding: 2rem;
|
||||
border: 2px dotted var(--primary-low);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.component-properties {
|
||||
width: 100%;
|
||||
|
||||
&__cell {
|
||||
padding: 0.5rem 0;
|
||||
|
||||
&:first-child {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
textarea,
|
||||
input {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
margin-bottom: 2em;
|
||||
|
|
|
@ -101,7 +101,10 @@ module SystemHelpers
|
|||
|
||||
ENV["TZ"] = timezone
|
||||
|
||||
using_session(timezone) { freeze_time(&example) }
|
||||
using_session(timezone) do |session|
|
||||
freeze_time(&example)
|
||||
session.quit
|
||||
end
|
||||
|
||||
ENV["TZ"] = previous_browser_timezone
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue