mirror of
https://github.com/discourse/discourse.git
synced 2025-03-06 11:19:51 +00:00
FIX: various mobile chat improvements (#22132)
- FIX: improves reactions and thread indicator touch event on mobile These "buttons" are located inside a scroll list which makes them very specific. The general idea is to ensure these events are passive and are not bubbling to the parent. - DEV: moves state on top level message node - FIX: ensures popover arrow has the correct border - FIX: makes a message expanded by default - FIX applies the same ios scroll fix on thread and channel - UI: better active/hover state for thread indicator - UI: attempts to follow more closely our BEM naming scheme - FIX: reduces bottom padding on message with thread indicator and user info hidden - UI: add padding for first message in thread - FIX: prevents actions backdrop to open thread - UI: makes thread indicator resizable
This commit is contained in:
parent
517c9e7782
commit
7dafd275ac
@ -28,6 +28,7 @@ registerUnbound("format-date", function (val, params) {
|
|||||||
format,
|
format,
|
||||||
title,
|
title,
|
||||||
leaveAgo,
|
leaveAgo,
|
||||||
|
prefix: params.prefix,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,11 @@ export function autoUpdatingRelativeAge(date, options) {
|
|||||||
append += "' title='" + longDate(date);
|
append += "' title='" + longDate(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let prefix = "";
|
||||||
|
if (options.prefix) {
|
||||||
|
prefix = options.prefix + " ";
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
"<span class='relative-date" +
|
"<span class='relative-date" +
|
||||||
append +
|
append +
|
||||||
@ -110,6 +115,7 @@ export function autoUpdatingRelativeAge(date, options) {
|
|||||||
"' data-format='" +
|
"' data-format='" +
|
||||||
format +
|
format +
|
||||||
"'>" +
|
"'>" +
|
||||||
|
prefix +
|
||||||
relAge +
|
relAge +
|
||||||
"</span>"
|
"</span>"
|
||||||
);
|
);
|
||||||
|
@ -260,6 +260,9 @@ module("Unit | Utility | formatter", function (hooks) {
|
|||||||
assert.strictEqual(elem.dataset.time, d.getTime().toString());
|
assert.strictEqual(elem.dataset.time, d.getTime().toString());
|
||||||
assert.strictEqual(elem.title, "");
|
assert.strictEqual(elem.title, "");
|
||||||
assert.strictEqual(elem.innerHTML, "1 day");
|
assert.strictEqual(elem.innerHTML, "1 day");
|
||||||
|
|
||||||
|
elem = domFromString(autoUpdatingRelativeAge(d, { prefix: "test" }))[0];
|
||||||
|
assert.strictEqual(elem.innerHTML, "test 1d");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("updateRelativeAge", function (assert) {
|
test("updateRelativeAge", function (assert) {
|
||||||
|
@ -15,8 +15,14 @@ $d-popover-border: var(--primary-low);
|
|||||||
top: -10px;
|
top: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tippy-svg-arrow > svg {
|
#tippy-rounded-arrow {
|
||||||
color: $d-popover-background;
|
.svg-content {
|
||||||
|
fill: $d-popover-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-arrow {
|
||||||
|
fill: $d-popover-border;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-tooltip] > *,
|
[data-tooltip] > *,
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
class="collapse-area"
|
class="collapse-area"
|
||||||
{{on "touchstart" this.collapseMenu passive=true bubbles=false}}
|
{{on "touchstart" this.collapseMenu passive=false bubbles=false}}
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ export default class ChatMessageActionsMobile extends Component {
|
|||||||
@tracked hasExpandedReply = false;
|
@tracked hasExpandedReply = false;
|
||||||
@tracked showFadeIn = false;
|
@tracked showFadeIn = false;
|
||||||
|
|
||||||
messageActions = null;
|
|
||||||
|
|
||||||
get message() {
|
get message() {
|
||||||
return this.chat.activeMessage.model;
|
return this.chat.activeMessage.model;
|
||||||
}
|
}
|
||||||
@ -49,7 +47,8 @@ export default class ChatMessageActionsMobile extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
collapseMenu() {
|
collapseMenu(event) {
|
||||||
|
event.preventDefault();
|
||||||
this.#onCloseMenu();
|
this.#onCloseMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
{{#if (and @reaction this.emojiUrl)}}
|
{{#if (and @reaction this.emojiUrl)}}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
{{on "click" this.handleClick bubbles=false}}
|
|
||||||
{{on "touchstart" this.handleClick bubbles=false}}
|
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class={{concat-class
|
class={{concat-class
|
||||||
"chat-message-reaction"
|
"chat-message-reaction"
|
||||||
(if @reaction.reacted "reacted")
|
(if @reaction.reacted "reacted")
|
||||||
|
(if this.isActive "-active")
|
||||||
}}
|
}}
|
||||||
data-emoji-name={{@reaction.emoji}}
|
data-emoji-name={{@reaction.emoji}}
|
||||||
data-tippy-content={{this.popoverContent}}
|
data-tippy-content={{this.popoverContent}}
|
||||||
title={{this.emojiString}}
|
title={{this.emojiString}}
|
||||||
{{did-insert this.setupTooltip}}
|
{{did-insert this.setup}}
|
||||||
{{will-destroy this.teardownTooltip}}
|
{{will-destroy this.teardown}}
|
||||||
{{did-update this.refreshTooltip this.popoverContent}}
|
{{did-update this.refreshTooltip this.popoverContent}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
|
@ -2,29 +2,116 @@ import Component from "@glimmer/component";
|
|||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { emojiUnescape, emojiUrlFor } from "discourse/lib/text";
|
import { emojiUnescape, emojiUrlFor } from "discourse/lib/text";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { schedule } from "@ember/runloop";
|
import { cancel } from "@ember/runloop";
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import setupPopover from "discourse/lib/d-popover";
|
import setupPopover from "discourse/lib/d-popover";
|
||||||
|
import discourseLater from "discourse-common/lib/later";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
|
||||||
export default class ChatMessageReaction extends Component {
|
export default class ChatMessageReaction extends Component {
|
||||||
|
@service capabilities;
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
|
|
||||||
|
@tracked isActive = false;
|
||||||
|
|
||||||
get showCount() {
|
get showCount() {
|
||||||
return this.args.showCount ?? true;
|
return this.args.showCount ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
setupTooltip(element) {
|
setup(element) {
|
||||||
if (this.args.showTooltip) {
|
this.setupListeners(element);
|
||||||
schedule("afterRender", () => {
|
this.setupTooltip(element);
|
||||||
this._tippyInstance?.destroy();
|
}
|
||||||
this._tippyInstance = setupPopover(element, {
|
|
||||||
interactive: false,
|
@action
|
||||||
allowHTML: true,
|
teardown() {
|
||||||
delay: 250,
|
cancel(this.longPressHandler);
|
||||||
});
|
this.teardownTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
setupListeners(element) {
|
||||||
|
this.element = element;
|
||||||
|
|
||||||
|
if (this.capabilities.touch) {
|
||||||
|
this.element.addEventListener("touchstart", this.onTouchStart, {
|
||||||
|
passive: true,
|
||||||
});
|
});
|
||||||
|
this.element.addEventListener("touchmove", this.cancelTouch, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
this.element.addEventListener("touchend", this.onTouchEnd);
|
||||||
|
this.element.addEventListener("touchCancel", this.cancelTouch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.element.addEventListener("click", this.handleClick, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
teardownListeners() {
|
||||||
|
if (this.capabilities.touch) {
|
||||||
|
this.element.removeEventListener("touchstart", this.onTouchStart, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
this.element.removeEventListener("touchmove", this.cancelTouch, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
this.element.removeEventListener("touchend", this.onTouchEnd);
|
||||||
|
this.element.removeEventListener("touchCancel", this.cancelTouch);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.removeEventListener("click", this.handleClick, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onTouchStart(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.isActive = true;
|
||||||
|
|
||||||
|
this.longPressHandler = discourseLater(() => {
|
||||||
|
this.touching = false;
|
||||||
|
}, 400);
|
||||||
|
|
||||||
|
this.touching = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
cancelTouch() {
|
||||||
|
cancel(this.longPressHandler);
|
||||||
|
this._tippyInstance?.hide();
|
||||||
|
this.touching = false;
|
||||||
|
this.isActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onTouchEnd(event) {
|
||||||
|
if (this.touching) {
|
||||||
|
this.handleClick(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(this.longPressHandler);
|
||||||
|
this._tippyInstance?.hide();
|
||||||
|
this.isActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
setupTooltip(element) {
|
||||||
|
this._tippyInstance = setupPopover(element, {
|
||||||
|
trigger: "mouseenter",
|
||||||
|
interactive: false,
|
||||||
|
allowHTML: true,
|
||||||
|
offset: [0, 10],
|
||||||
|
onShow(instance) {
|
||||||
|
if (instance.props.content === "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@ -34,7 +121,7 @@ export default class ChatMessageReaction extends Component {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
refreshTooltip() {
|
refreshTooltip() {
|
||||||
this._tippyInstance?.setContent(this.popoverContent);
|
this._tippyInstance?.setContent(this.popoverContent || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
get emojiString() {
|
get emojiString() {
|
||||||
@ -53,6 +140,8 @@ export default class ChatMessageReaction extends Component {
|
|||||||
this.args.reaction.emoji,
|
this.args.reaction.emoji,
|
||||||
this.args.reaction.reacted ? "remove" : "add"
|
this.args.reaction.reacted ? "remove" : "add"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._tippyInstance?.clearDelayTimeouts();
|
||||||
}
|
}
|
||||||
|
|
||||||
get popoverContent() {
|
get popoverContent() {
|
||||||
@ -97,7 +186,7 @@ export default class ChatMessageReaction extends Component {
|
|||||||
if (unnamedUserCount > 0) {
|
if (unnamedUserCount > 0) {
|
||||||
return I18n.t("chat.reactions.you_multiple_users_and_more", {
|
return I18n.t("chat.reactions.you_multiple_users_and_more", {
|
||||||
emoji: this.args.reaction.emoji,
|
emoji: this.args.reaction.emoji,
|
||||||
commaSeparatedUsernames: this._joinUsernames(usernames),
|
commaSeparatedUsernames: this.#joinUsernames(usernames),
|
||||||
count: unnamedUserCount,
|
count: unnamedUserCount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -105,7 +194,7 @@ export default class ChatMessageReaction extends Component {
|
|||||||
return I18n.t("chat.reactions.you_and_multiple_users", {
|
return I18n.t("chat.reactions.you_and_multiple_users", {
|
||||||
emoji: this.args.reaction.emoji,
|
emoji: this.args.reaction.emoji,
|
||||||
username: usernames.pop(),
|
username: usernames.pop(),
|
||||||
commaSeparatedUsernames: this._joinUsernames(usernames),
|
commaSeparatedUsernames: this.#joinUsernames(usernames),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +223,7 @@ export default class ChatMessageReaction extends Component {
|
|||||||
if (unnamedUserCount > 0) {
|
if (unnamedUserCount > 0) {
|
||||||
return I18n.t("chat.reactions.multiple_users_and_more", {
|
return I18n.t("chat.reactions.multiple_users_and_more", {
|
||||||
emoji: this.args.reaction.emoji,
|
emoji: this.args.reaction.emoji,
|
||||||
commaSeparatedUsernames: this._joinUsernames(usernames),
|
commaSeparatedUsernames: this.#joinUsernames(usernames),
|
||||||
count: unnamedUserCount,
|
count: unnamedUserCount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -142,11 +231,11 @@ export default class ChatMessageReaction extends Component {
|
|||||||
return I18n.t("chat.reactions.multiple_users", {
|
return I18n.t("chat.reactions.multiple_users", {
|
||||||
emoji: this.args.reaction.emoji,
|
emoji: this.args.reaction.emoji,
|
||||||
username: usernames.pop(),
|
username: usernames.pop(),
|
||||||
commaSeparatedUsernames: this._joinUsernames(usernames),
|
commaSeparatedUsernames: this.#joinUsernames(usernames),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_joinUsernames(usernames) {
|
#joinUsernames(usernames) {
|
||||||
return usernames.join(I18n.t("word_connector.comma"));
|
return usernames.join(I18n.t("word_connector.comma"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
<LinkTo
|
<div
|
||||||
@route="chat.channel.thread"
|
class={{concat-class
|
||||||
@models={{@message.thread.routeModels}}
|
"chat-message-thread-indicator"
|
||||||
class="chat-message-thread-indicator"
|
(if this.isActive "-active")
|
||||||
|
}}
|
||||||
|
{{did-insert this.setup}}
|
||||||
|
{{will-destroy this.teardown}}
|
||||||
|
role="button"
|
||||||
|
title={{i18n "chat.threads.open"}}
|
||||||
>
|
>
|
||||||
{{#unless this.chatStateManager.isDrawerActive}}
|
{{#unless this.chatStateManager.isDrawerActive}}
|
||||||
<div class="chat-message-thread-indicator__last-reply-avatar">
|
<div class="chat-message-thread-indicator__last-reply-avatar">
|
||||||
@ -28,14 +33,14 @@
|
|||||||
{{@message.thread.preview.lastReplyUser.username}}
|
{{@message.thread.preview.lastReplyUser.username}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span><span class="chat-message-thread-indicator__last-reply-label">{{i18n
|
<span class="chat-message-thread-indicator__last-reply-container">
|
||||||
"chat.thread.last_reply"
|
{{format-date
|
||||||
}}</span>{{format-date
|
|
||||||
@message.thread.preview.lastReplyCreatedAt
|
@message.thread.preview.lastReplyCreatedAt
|
||||||
leaveAgo="true"
|
leaveAgo="true"
|
||||||
}}</span>
|
prefix=(i18n "chat.thread.last_reply")
|
||||||
|
}}
|
||||||
|
|
</span>
|
||||||
|
<span class="separator">|</span>
|
||||||
<span class="chat-message-thread-indicator__replies-count">
|
<span class="chat-message-thread-indicator__replies-count">
|
||||||
{{i18n "chat.thread.replies" count=@message.thread.preview.replyCount}}
|
{{i18n "chat.thread.replies" count=@message.thread.preview.replyCount}}
|
||||||
</span>
|
</span>
|
||||||
@ -49,4 +54,4 @@
|
|||||||
<div class="chat-message-thread-indicator__participants">
|
<div class="chat-message-thread-indicator__participants">
|
||||||
<Chat::Thread::Participants @thread={{@message.thread}} />
|
<Chat::Thread::Participants @thread={{@message.thread}} />
|
||||||
</div>
|
</div>
|
||||||
</LinkTo>
|
</div>
|
@ -1,10 +1,89 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import { escapeExpression } from "discourse/lib/utilities";
|
import { escapeExpression } from "discourse/lib/utilities";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
|
||||||
export default class ChatMessageThreadIndicator extends Component {
|
export default class ChatMessageThreadIndicator extends Component {
|
||||||
@service site;
|
@service capabilities;
|
||||||
|
@service chat;
|
||||||
@service chatStateManager;
|
@service chatStateManager;
|
||||||
|
@service router;
|
||||||
|
@service site;
|
||||||
|
|
||||||
|
@tracked isActive = false;
|
||||||
|
|
||||||
|
@action
|
||||||
|
setup(element) {
|
||||||
|
this.element = element;
|
||||||
|
|
||||||
|
if (this.capabilities.touch) {
|
||||||
|
this.element.addEventListener("touchstart", this.onTouchStart, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
this.element.addEventListener("touchmove", this.cancelTouch, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
this.element.addEventListener("touchend", this.onTouchEnd);
|
||||||
|
this.element.addEventListener("touchCancel", this.cancelTouch);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.addEventListener("click", this.openThread, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
teardown() {
|
||||||
|
if (this.capabilities.touch) {
|
||||||
|
this.element.removeEventListener("touchstart", this.onTouchStart, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
this.element.removeEventListener("touchmove", this.cancelTouch, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
this.element.removeEventListener("touchend", this.onTouchEnd);
|
||||||
|
this.element.removeEventListener("touchCancel", this.cancelTouch);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.removeEventListener("click", this.openThread, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
onTouchStart(event) {
|
||||||
|
this.isActive = true;
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
this.touching = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
onTouchEnd() {
|
||||||
|
this.isActive = false;
|
||||||
|
|
||||||
|
if (this.touching) {
|
||||||
|
this.openThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
cancelTouch() {
|
||||||
|
this.isActive = false;
|
||||||
|
this.touching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
openThread() {
|
||||||
|
this.chat.activeMessage = null;
|
||||||
|
|
||||||
|
this.router.transitionTo(
|
||||||
|
"chat.channel.thread",
|
||||||
|
...this.args.message.thread.routeModels
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
get threadTitle() {
|
get threadTitle() {
|
||||||
return escapeExpression(this.args.message.threadTitle);
|
return escapeExpression(this.args.message.threadTitle);
|
||||||
|
@ -7,10 +7,27 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
{{will-destroy this.teardownChatMessage}}
|
class={{concat-class
|
||||||
{{did-insert this.decorateCookedMessage}}
|
"chat-message-container"
|
||||||
{{did-update this.decorateCookedMessage @message.id}}
|
(if this.pane.selectingMessages "-selectable")
|
||||||
{{did-update this.decorateCookedMessage @message.version}}
|
(if @message.highlighted "-highlighted")
|
||||||
|
(if (eq @message.user.id this.currentUser.id) "is-by-current-user")
|
||||||
|
(if @message.staged "-staged" "-persisted")
|
||||||
|
(if this.hasActiveState "-active")
|
||||||
|
(if @message.bookmark "-bookmarked")
|
||||||
|
(if @message.deletedAt "-deleted")
|
||||||
|
(if @message.selected "-selected")
|
||||||
|
(if @message.error "-errored")
|
||||||
|
(if this.showThreadIndicator "has-thread-indicator")
|
||||||
|
(if this.hideUserInfo "-user-info-hidden")
|
||||||
|
(if this.hasReply "has-reply")
|
||||||
|
}}
|
||||||
|
data-id={{@message.id}}
|
||||||
|
data-thread-id={{@message.thread.id}}
|
||||||
|
{{did-insert this.didInsertMessage}}
|
||||||
|
{{did-update this.didUpdateMessageId @message.id}}
|
||||||
|
{{did-update this.didUpdateMessageVersion @message.version}}
|
||||||
|
{{will-destroy this.willDestroyMessage}}
|
||||||
{{on "mouseenter" this.onMouseEnter passive=true}}
|
{{on "mouseenter" this.onMouseEnter passive=true}}
|
||||||
{{on "mouseleave" this.onMouseLeave passive=true}}
|
{{on "mouseleave" this.onMouseLeave passive=true}}
|
||||||
{{on "mousemove" this.onMouseMove passive=true}}
|
{{on "mousemove" this.onMouseMove passive=true}}
|
||||||
@ -19,18 +36,6 @@
|
|||||||
this.handleLongPressEnd
|
this.handleLongPressEnd
|
||||||
this.onLongPressCancel
|
this.onLongPressCancel
|
||||||
}}
|
}}
|
||||||
class={{concat-class
|
|
||||||
"chat-message-container"
|
|
||||||
(if this.pane.selectingMessages "selecting-messages")
|
|
||||||
(if @message.highlighted "highlighted")
|
|
||||||
(if (eq @message.user.id this.currentUser.id) "is-by-current-user")
|
|
||||||
(if @message.staged "is-staged" "is-persisted")
|
|
||||||
(if @message.deletedAt "is-deleted")
|
|
||||||
(if (eq @message.id this.chat.activeMessage.model.id) "is-active")
|
|
||||||
}}
|
|
||||||
data-id={{@message.id}}
|
|
||||||
data-thread-id={{@message.thread.id}}
|
|
||||||
data-selected={{@message.selected}}
|
|
||||||
{{chat/track-message
|
{{chat/track-message
|
||||||
(hash
|
(hash
|
||||||
didEnterViewport=(fn @messageDidEnterViewport @message)
|
didEnterViewport=(fn @messageDidEnterViewport @message)
|
||||||
@ -65,20 +70,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div
|
<div class="chat-message">
|
||||||
{{did-insert this.refreshStatusOnMentions}}
|
|
||||||
{{did-update this.refreshStatusOnMentions @message.version}}
|
|
||||||
{{did-update this.initMentionedUsers @message.version}}
|
|
||||||
class={{concat-class
|
|
||||||
"chat-message"
|
|
||||||
(if @message.deletedAt "deleted")
|
|
||||||
(if (and @message.inReplyTo (not this.hideReplyToInfo)) "is-reply")
|
|
||||||
(if this.showThreadIndicator "is-threaded")
|
|
||||||
(if this.hideUserInfo "user-info-hidden")
|
|
||||||
(if @message.error "errored")
|
|
||||||
(if @message.bookmark "chat-message-bookmarked")
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{{#unless this.hideReplyToInfo}}
|
{{#unless this.hideReplyToInfo}}
|
||||||
<ChatMessageInReplyToIndicator @message={{@message}} />
|
<ChatMessageInReplyToIndicator @message={{@message}} />
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
@ -195,9 +187,9 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if this.mentionWarning.groups_with_too_many_members}}
|
{{#if this.mentionWarning.groups_with_too_many_members}}
|
||||||
<p
|
<p class="warning-item">
|
||||||
class="warning-item"
|
{{this.groupsWithTooManyMembers}}
|
||||||
>{{this.groupsWithTooManyMembers}}</p>
|
</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,6 +12,7 @@ import ChatMessageInteractor from "discourse/plugins/chat/discourse/lib/chat-mes
|
|||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
import { updateUserStatusOnMention } from "discourse/lib/update-user-status-on-mention";
|
import { updateUserStatusOnMention } from "discourse/lib/update-user-status-on-mention";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
|
||||||
let _chatMessageDecorators = [];
|
let _chatMessageDecorators = [];
|
||||||
|
|
||||||
@ -41,6 +42,8 @@ export default class ChatMessage extends Component {
|
|||||||
@service chatChannelsManager;
|
@service chatChannelsManager;
|
||||||
@service router;
|
@service router;
|
||||||
|
|
||||||
|
@tracked isActive = false;
|
||||||
|
|
||||||
@optionalService adminTools;
|
@optionalService adminTools;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -123,7 +126,7 @@ export default class ChatMessage extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
teardownChatMessage() {
|
willDestroyMessage() {
|
||||||
cancel(this._invitationSentTimer);
|
cancel(this._invitationSentTimer);
|
||||||
cancel(this._disableMessageActionsHandler);
|
cancel(this._disableMessageActionsHandler);
|
||||||
this.#teardownMentionedUsers();
|
this.#teardownMentionedUsers();
|
||||||
@ -132,10 +135,6 @@ export default class ChatMessage extends Component {
|
|||||||
@action
|
@action
|
||||||
refreshStatusOnMentions() {
|
refreshStatusOnMentions() {
|
||||||
schedule("afterRender", () => {
|
schedule("afterRender", () => {
|
||||||
if (!this.messageContainer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.args.message.mentionedUsers.forEach((user) => {
|
this.args.message.mentionedUsers.forEach((user) => {
|
||||||
const href = `/u/${user.username.toLowerCase()}`;
|
const href = `/u/${user.username.toLowerCase()}`;
|
||||||
const mentions = this.messageContainer.querySelectorAll(
|
const mentions = this.messageContainer.querySelectorAll(
|
||||||
@ -149,13 +148,28 @@ export default class ChatMessage extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
didInsertMessage(element) {
|
||||||
|
this.messageContainer = element;
|
||||||
|
this.decorateCookedMessage();
|
||||||
|
this.refreshStatusOnMentions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
didUpdateMessageId() {
|
||||||
|
this.decorateCookedMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
didUpdateMessageVersion() {
|
||||||
|
this.decorateCookedMessage();
|
||||||
|
this.refreshStatusOnMentions();
|
||||||
|
this.initMentionedUsers();
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
decorateCookedMessage() {
|
decorateCookedMessage() {
|
||||||
schedule("afterRender", () => {
|
schedule("afterRender", () => {
|
||||||
if (!this.messageContainer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_chatMessageDecorators.forEach((decorator) => {
|
_chatMessageDecorators.forEach((decorator) => {
|
||||||
decorator.call(this, this.messageContainer, this.args.message.channel);
|
decorator.call(this, this.messageContainer, this.args.message.channel);
|
||||||
});
|
});
|
||||||
@ -174,13 +188,6 @@ export default class ChatMessage extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get messageContainer() {
|
|
||||||
const id = this.args.message?.id;
|
|
||||||
if (id) {
|
|
||||||
return document.querySelector(`.chat-message-container[data-id='${id}']`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get show() {
|
get show() {
|
||||||
return (
|
return (
|
||||||
!this.args.message?.deletedAt ||
|
!this.args.message?.deletedAt ||
|
||||||
@ -251,6 +258,10 @@ export default class ChatMessage extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.args.message.expanded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.chat.activeMessage = {
|
this.chat.activeMessage = {
|
||||||
model: this.args.message,
|
model: this.args.message,
|
||||||
context: this.args.context,
|
context: this.args.context,
|
||||||
@ -258,13 +269,17 @@ export default class ChatMessage extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
handleLongPressStart(element) {
|
handleLongPressStart() {
|
||||||
element.classList.add("is-long-pressed");
|
if (!this.args.message.expanded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
onLongPressCancel(element) {
|
onLongPressCancel() {
|
||||||
element.classList.remove("is-long-pressed");
|
this.isActive = false;
|
||||||
|
|
||||||
// this a tricky bit of code which is needed to prevent the long press
|
// this a tricky bit of code which is needed to prevent the long press
|
||||||
// from triggering a click on the message actions panel when releasing finger press
|
// from triggering a click on the message actions panel when releasing finger press
|
||||||
@ -279,8 +294,8 @@ export default class ChatMessage extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
handleLongPressEnd(element) {
|
handleLongPressEnd() {
|
||||||
element.classList.remove("is-long-pressed");
|
this.isActive = false;
|
||||||
|
|
||||||
if (isZoomed()) {
|
if (isZoomed()) {
|
||||||
// if zoomed don't handle long press
|
// if zoomed don't handle long press
|
||||||
@ -294,6 +309,17 @@ export default class ChatMessage extends Component {
|
|||||||
this._setActiveMessage();
|
this._setActiveMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get hasActiveState() {
|
||||||
|
return (
|
||||||
|
this.isActive ||
|
||||||
|
this.chat.activeMessage?.model?.id === this.args.message.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasReply() {
|
||||||
|
return this.args.inReplyTo && !this.hideReplyToInfo;
|
||||||
|
}
|
||||||
|
|
||||||
get hideUserInfo() {
|
get hideUserInfo() {
|
||||||
const message = this.args.message;
|
const message = this.args.message;
|
||||||
|
|
||||||
|
@ -327,31 +327,22 @@ export default class ChatThreadPanel extends Component {
|
|||||||
// we now use this hack to disable it
|
// we now use this hack to disable it
|
||||||
@bind
|
@bind
|
||||||
forceRendering(callback) {
|
forceRendering(callback) {
|
||||||
schedule("afterRender", () => {
|
if (this.capabilities.isIOS) {
|
||||||
if (this._selfDeleted) {
|
this.scrollable.style.overflow = "hidden";
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.scrollable) {
|
callback?.();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.capabilities.isIOS) {
|
if (this.capabilities.isIOS) {
|
||||||
this.scrollable.style.overflow = "hidden";
|
next(() => {
|
||||||
}
|
schedule("afterRender", () => {
|
||||||
|
if (this._selfDeleted || !this.scrollable) {
|
||||||
callback?.();
|
|
||||||
|
|
||||||
if (this.capabilities.isIOS) {
|
|
||||||
discourseLater(() => {
|
|
||||||
if (!this.scrollable) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scrollable.style.overflow = "auto";
|
this.scrollable.style.overflow = "auto";
|
||||||
}, 50);
|
});
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -29,14 +29,13 @@ export default class ChatMessage {
|
|||||||
@tracked draft;
|
@tracked draft;
|
||||||
@tracked channelId;
|
@tracked channelId;
|
||||||
@tracked createdAt;
|
@tracked createdAt;
|
||||||
@tracked deletedAt;
|
|
||||||
@tracked uploads;
|
@tracked uploads;
|
||||||
@tracked excerpt;
|
@tracked excerpt;
|
||||||
@tracked reactions;
|
@tracked reactions;
|
||||||
@tracked reviewableId;
|
@tracked reviewableId;
|
||||||
@tracked user;
|
@tracked user;
|
||||||
@tracked inReplyTo;
|
@tracked inReplyTo;
|
||||||
@tracked expanded;
|
@tracked expanded = true;
|
||||||
@tracked bookmark;
|
@tracked bookmark;
|
||||||
@tracked userFlagStatus;
|
@tracked userFlagStatus;
|
||||||
@tracked hidden;
|
@tracked hidden;
|
||||||
@ -54,6 +53,7 @@ export default class ChatMessage {
|
|||||||
@tracked manager;
|
@tracked manager;
|
||||||
@tracked threadTitle;
|
@tracked threadTitle;
|
||||||
|
|
||||||
|
@tracked _deletedAt;
|
||||||
@tracked _cooked;
|
@tracked _cooked;
|
||||||
|
|
||||||
constructor(channel, args = {}) {
|
constructor(channel, args = {}) {
|
||||||
@ -69,7 +69,9 @@ export default class ChatMessage {
|
|||||||
this.hidden = args.hidden || false;
|
this.hidden = args.hidden || false;
|
||||||
this.chatWebhookEvent = args.chatWebhookEvent || args.chat_webhook_event;
|
this.chatWebhookEvent = args.chatWebhookEvent || args.chat_webhook_event;
|
||||||
this.createdAt = args.createdAt || args.created_at;
|
this.createdAt = args.createdAt || args.created_at;
|
||||||
this.deletedAt = args.deletedAt || args.deleted_at;
|
this._deletedAt = args.deletedAt || args.deleted_at;
|
||||||
|
this.expanded =
|
||||||
|
this.hidden || this._deletedAt ? false : args.expanded || true;
|
||||||
this.excerpt = args.excerpt;
|
this.excerpt = args.excerpt;
|
||||||
this.reviewableId = args.reviewableId || args.reviewable_id;
|
this.reviewableId = args.reviewableId || args.reviewable_id;
|
||||||
this.userFlagStatus = args.userFlagStatus || args.user_flag_status;
|
this.userFlagStatus = args.userFlagStatus || args.user_flag_status;
|
||||||
@ -130,6 +132,16 @@ export default class ChatMessage {
|
|||||||
return !this.staged && !this.error;
|
return !this.staged && !this.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get deletedAt() {
|
||||||
|
return this._deletedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
set deletedAt(value) {
|
||||||
|
this._deletedAt = value;
|
||||||
|
this.incrementVersion();
|
||||||
|
return this._deletedAt;
|
||||||
|
}
|
||||||
|
|
||||||
get cooked() {
|
get cooked() {
|
||||||
return this._cooked;
|
return this._cooked;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import discourseLater from "discourse-common/lib/later";
|
|||||||
|
|
||||||
function cancelEvent(event) {
|
function cancelEvent(event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ChatOnLongPress extends Modifier {
|
export default class ChatOnLongPress extends Modifier {
|
||||||
@ -33,7 +32,7 @@ export default class ChatOnLongPress extends Modifier {
|
|||||||
this.onLongPressEnd = onLongPressEnd || (() => {});
|
this.onLongPressEnd = onLongPressEnd || (() => {});
|
||||||
this.onLongPressCancel = onLongPressCancel || (() => {});
|
this.onLongPressCancel = onLongPressCancel || (() => {});
|
||||||
|
|
||||||
element.addEventListener("touchstart", this.handleTouchStart, {
|
this.element.addEventListener("touchstart", this.handleTouchStart, {
|
||||||
passive: true,
|
passive: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -42,11 +41,13 @@ export default class ChatOnLongPress extends Modifier {
|
|||||||
onCancel() {
|
onCancel() {
|
||||||
cancel(this.timeout);
|
cancel(this.timeout);
|
||||||
|
|
||||||
this.element.removeEventListener("touchmove", this.onCancel, {
|
if (this.capabilities.touch) {
|
||||||
passive: true,
|
this.element.removeEventListener("touchmove", this.onCancel, {
|
||||||
});
|
passive: true,
|
||||||
this.element.removeEventListener("touchend", this.onCancel);
|
});
|
||||||
this.element.removeEventListener("touchcancel", this.onCancel);
|
this.element.removeEventListener("touchend", this.onCancel);
|
||||||
|
this.element.removeEventListener("touchcancel", this.onCancel);
|
||||||
|
}
|
||||||
|
|
||||||
this.onLongPressCancel(this.element);
|
this.onLongPressCancel(this.element);
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ html.ios-device.keyboard-visible body #main-outlet .full-page-chat {
|
|||||||
.chat-message-container {
|
.chat-message-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
||||||
&.selecting-messages {
|
&.-selectable {
|
||||||
grid-template-columns: 1.5em 1fr;
|
grid-template-columns: 1.5em 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ html.ios-device.keyboard-visible body #main-outlet .full-page-chat {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.chat-message:not(.is-reply) & {
|
.chat-message-container:not(.has-reply) & {
|
||||||
width: var(--message-left-width);
|
width: var(--message-left-width);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,25 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
|
|
||||||
|
> * {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
&:visited,
|
&:visited,
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
.touch & {
|
||||||
box-shadow: var(--shadow-dropdown-lite);
|
&.-active {
|
||||||
|
box-shadow: var(--shadow-dropdown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-touch & {
|
||||||
|
&:hover {
|
||||||
|
box-shadow: var(--shadow-dropdown);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__participants {
|
&__participants {
|
||||||
@ -35,20 +47,34 @@
|
|||||||
|
|
||||||
&__last-reply-metadata {
|
&__last-reply-metadata {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-end;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
color: var(--primary-medium);
|
color: var(--primary-medium);
|
||||||
font-size: var(--font-down-1);
|
|
||||||
|
.separator {
|
||||||
|
font-size: var(--font-down-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__last-reply-label {
|
&__last-reply-container {
|
||||||
margin-right: 0.25rem;
|
display: inline-flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
font-size: var(--font-down-1);
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.relative-date {
|
||||||
|
@include ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__last-reply-user {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--secondary-low);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__last-reply-username {
|
&__last-reply-username {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--secondary-low);
|
color: var(--secondary-low);
|
||||||
font-size: var(--font-up-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__last-reply-excerpt {
|
&__last-reply-excerpt {
|
||||||
@ -62,7 +88,15 @@
|
|||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.chat-message-thread-indicator__replies-count {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__replies-count {
|
&__replies-count {
|
||||||
|
@include ellipsis;
|
||||||
color: var(--tertiary);
|
color: var(--tertiary);
|
||||||
|
font-size: var(--font-down-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,56 +28,10 @@
|
|||||||
@include chat-reaction;
|
@include chat-reaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.chat-action {
|
|
||||||
background-color: var(--highlight-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.errored {
|
|
||||||
color: var(--primary-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.deleted {
|
|
||||||
background-color: var(--danger-low);
|
|
||||||
}
|
|
||||||
|
|
||||||
.not-mobile-device &.deleted:hover {
|
.not-mobile-device &.deleted:hover {
|
||||||
background-color: var(--danger-hover);
|
background-color: var(--danger-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-reply {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: var(--message-left-width) 1fr;
|
|
||||||
grid-template-rows: 30px auto;
|
|
||||||
grid-template-areas:
|
|
||||||
"replyto replyto"
|
|
||||||
"avatar message";
|
|
||||||
|
|
||||||
.chat-user-avatar {
|
|
||||||
grid-area: avatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-content {
|
|
||||||
grid-area: message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-threaded {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: var(--message-left-width) 1fr;
|
|
||||||
grid-template-rows: auto;
|
|
||||||
grid-template-areas:
|
|
||||||
"avatar message"
|
|
||||||
"threadindicator threadindicator";
|
|
||||||
padding: 0.65rem 1rem !important;
|
|
||||||
.chat-user-avatar {
|
|
||||||
grid-area: avatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-content {
|
|
||||||
grid-area: message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-content {
|
.chat-message-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -200,77 +154,105 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message-container.highlighted .chat-message {
|
.touch .chat-message-container {
|
||||||
background-color: var(--tertiary-low) !important;
|
&.-active {
|
||||||
|
background: var(--d-hover);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
&.-bookmarked {
|
||||||
|
background: var(--highlight-low);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-messages-container {
|
.no-touch .chat-message-container {
|
||||||
.chat-message {
|
&:hover,
|
||||||
&.chat-message-bookmarked {
|
&-active {
|
||||||
background: var(--highlight-bg);
|
background: var(--d-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
.chat-message-reaction-list .chat-message-react-btn {
|
.chat-message-reaction-list .chat-message-react-btn {
|
||||||
display: none;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message-container {
|
&.-active,
|
||||||
background-color: var(--secondary);
|
&:hover {
|
||||||
|
&.-bookmarked {
|
||||||
.touch & {
|
background: var(--highlight-medium);
|
||||||
&:active,
|
|
||||||
&.is-active {
|
|
||||||
background: var(--d-hover);
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.chat-message-bookmarked {
|
|
||||||
&:active,
|
|
||||||
&.is-active {
|
|
||||||
background: var(--highlight-low);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-touch & {
|
&.-deleted {
|
||||||
&.is-active,
|
background-color: var(--danger-medium);
|
||||||
&:hover,
|
}
|
||||||
&:active {
|
|
||||||
background: var(--d-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
&.-highlighted {
|
||||||
.chat-message-react-btn {
|
background-color: var(--tertiary-medium);
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.chat-message-bookmarked {
|
|
||||||
&.is-active,
|
|
||||||
&:hover {
|
|
||||||
background: var(--highlight-medium);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message-flagged {
|
.chat-message-container {
|
||||||
display: inline-block;
|
background-color: var(--secondary);
|
||||||
color: var(--danger);
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 0.3em;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.flag-count,
|
&.-errored {
|
||||||
.d-icon {
|
color: var(--primary-medium);
|
||||||
color: var(--danger);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.chat-action-text {
|
&.-deleted {
|
||||||
font-style: italic;
|
background-color: var(--danger-low);
|
||||||
|
padding-block: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-bookmarked {
|
||||||
|
background: var(--highlight-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-highlighted {
|
||||||
|
background-color: var(--tertiary-low);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-reply {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--message-left-width) 1fr;
|
||||||
|
grid-template-rows: 30px auto;
|
||||||
|
grid-template-areas:
|
||||||
|
"replyto replyto"
|
||||||
|
"avatar message";
|
||||||
|
|
||||||
|
.chat-user-avatar {
|
||||||
|
grid-area: avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message-content {
|
||||||
|
grid-area: message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-thread-indicator {
|
||||||
|
.chat-message {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--message-left-width) 1fr;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
grid-template-areas:
|
||||||
|
"avatar message"
|
||||||
|
"threadindicator threadindicator";
|
||||||
|
padding-bottom: 0.65rem !important;
|
||||||
|
|
||||||
|
.chat-user-avatar {
|
||||||
|
grid-area: avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message-content {
|
||||||
|
grid-area: message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message-reaction-list .chat-message-react-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.has-full-page-chat .chat-message .onebox:not(img),
|
.has-full-page-chat .chat-message .onebox:not(img),
|
||||||
@ -333,7 +315,7 @@
|
|||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active {
|
.-active & {
|
||||||
background: none !important;
|
background: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,23 +21,17 @@
|
|||||||
|
|
||||||
.chat-channel {
|
.chat-channel {
|
||||||
.chat-messages-container {
|
.chat-messages-container {
|
||||||
.chat-message {
|
&.has-reply {
|
||||||
&.is-reply {
|
grid-template-columns: var(--message-left-width) 1fr;
|
||||||
grid-template-columns: var(--message-left-width) 1fr;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.chat-user {
|
.chat-user {
|
||||||
width: var(--message-left-width);
|
width: var(--message-left-width);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message:not(.user-info-hidden) {
|
|
||||||
padding: 0.65rem 1rem 0.15rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-text {
|
.chat-message-text {
|
||||||
img:not(.emoji):not(.avatar, .onebox-avatar-inline) {
|
img:not(.emoji):not(.avatar, .onebox-avatar-inline) {
|
||||||
transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
|
transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||||
@ -51,8 +45,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message.user-info-hidden {
|
.chat-message-container:not(.-user-info-hidden) {
|
||||||
padding: 0.15rem 1rem;
|
.chat-message {
|
||||||
|
padding: 0.65rem 1rem 0.15rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message-container.-user-info-hidden {
|
||||||
|
.chat-message {
|
||||||
|
padding: 0.15rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-time {
|
.chat-time {
|
||||||
color: var(--secondary-medium);
|
color: var(--secondary-medium);
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
.chat-message-thread-indicator {
|
.chat-message-thread-indicator {
|
||||||
width: min-content;
|
|
||||||
min-width: 400px;
|
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
|
|
||||||
.chat-drawer & {
|
.chat-drawer & {
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
--channel-list-avatar-size: 38px;
|
--channel-list-avatar-size: 38px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message:not(.user-info-hidden) {
|
.chat-message-container:not(.-user-info-hidden) {
|
||||||
padding-top: 0.75em;
|
.chat-message {
|
||||||
|
padding-top: 0.75em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html.has-full-page-chat {
|
html.has-full-page-chat {
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
transition: transform 400ms;
|
transition: transform 400ms;
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
|
|
||||||
&.is-long-pressed {
|
&.-active {
|
||||||
animation: scale-animation 400ms;
|
animation: scale-animation 400ms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
.chat-thread {
|
.chat-thread {
|
||||||
&__body {
|
&__body {
|
||||||
margin: 0 1px 0 0;
|
margin: 0 1px 0 0;
|
||||||
padding: 0 10px;
|
}
|
||||||
|
|
||||||
|
.chat-messages-scroll {
|
||||||
|
padding: 10px 10px 0 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ RSpec.describe "Chat message - channel", type: :system do
|
|||||||
channel.hover_message(message_1)
|
channel.hover_message(message_1)
|
||||||
|
|
||||||
expect(page).to have_css(
|
expect(page).to have_css(
|
||||||
".chat-channel[data-id='#{channel_1.id}'] .chat-message-container[data-id='#{message_1.id}'].is-active",
|
".chat-channel[data-id='#{channel_1.id}'] .chat-message-container[data-id='#{message_1.id}'].-active",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -30,7 +30,7 @@ RSpec.describe "Chat message - thread", type: :system do
|
|||||||
thread_page.hover_message(first_message)
|
thread_page.hover_message(first_message)
|
||||||
|
|
||||||
expect(page).to have_css(
|
expect(page).to have_css(
|
||||||
".chat-thread[data-id='#{thread_1.id}'] [data-id='#{first_message.id}'].chat-message-container.is-active",
|
".chat-thread[data-id='#{thread_1.id}'] [data-id='#{first_message.id}'].chat-message-container.-active",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -19,7 +19,7 @@ RSpec.describe "Deleted message", type: :system do
|
|||||||
chat_page.visit_channel(channel_1)
|
chat_page.visit_channel(channel_1)
|
||||||
channel_page.send_message("aaaaaaaaaaaaaaaaaaaa")
|
channel_page.send_message("aaaaaaaaaaaaaaaaaaaa")
|
||||||
|
|
||||||
expect(page).to have_css(".is-persisted")
|
expect(page).to have_css(".-persisted")
|
||||||
|
|
||||||
last_message = find(".chat-message-container:last-child")
|
last_message = find(".chat-message-container:last-child")
|
||||||
channel_page.delete_message(OpenStruct.new(id: last_message["data-id"]))
|
channel_page.delete_message(OpenStruct.new(id: last_message["data-id"]))
|
||||||
|
@ -68,7 +68,7 @@ module PageObjects
|
|||||||
end
|
end
|
||||||
|
|
||||||
def click_message_action_mobile(message, message_action)
|
def click_message_action_mobile(message, message_action)
|
||||||
expand_message_actions_mobile(message, delay: 0.6)
|
expand_message_actions_mobile(message, delay: 0.4)
|
||||||
find(".chat-message-actions [data-id=\"#{message_action}\"]").click
|
find(".chat-message-actions [data-id=\"#{message_action}\"]").click
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -143,7 +143,7 @@ module PageObjects
|
|||||||
end
|
end
|
||||||
|
|
||||||
def has_bookmarked_message?(message)
|
def has_bookmarked_message?(message)
|
||||||
within(message_by_id(message.id)) { find(".chat-message-bookmarked") }
|
find(message_by_id_selector(message.id) + ".-bookmarked")
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_reaction(message, emoji)
|
def find_reaction(message, emoji)
|
||||||
|
@ -22,7 +22,7 @@ module PageObjects
|
|||||||
end
|
end
|
||||||
|
|
||||||
def select(shift: false)
|
def select(shift: false)
|
||||||
if component[:class].include?("selecting-message")
|
if component[:class].include?("-selectable")
|
||||||
message_selector = component.find(".chat-message-selector")
|
message_selector = component.find(".chat-message-selector")
|
||||||
if shift
|
if shift
|
||||||
message_selector.click(:shift)
|
message_selector.click(:shift)
|
||||||
@ -85,10 +85,10 @@ module PageObjects
|
|||||||
def build_selector(**args)
|
def build_selector(**args)
|
||||||
selector = SELECTOR
|
selector = SELECTOR
|
||||||
selector += "[data-id=\"#{args[:id]}\"]" if args[:id]
|
selector += "[data-id=\"#{args[:id]}\"]" if args[:id]
|
||||||
selector += "[data-selected]" if args[:selected]
|
selector += ".-selected" if args[:selected]
|
||||||
selector += ".is-persisted" if args[:persisted]
|
selector += ".-persisted" if args[:persisted]
|
||||||
selector += ".is-staged" if args[:staged]
|
selector += ".-staged" if args[:staged]
|
||||||
selector += ".is-deleted" if args[:deleted]
|
selector += ".-deleted" if args[:deleted]
|
||||||
selector
|
selector
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -45,8 +45,6 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
channel,
|
channel,
|
||||||
afterExpand: () => {},
|
|
||||||
onHoverMessage: () => {},
|
|
||||||
messageDidEnterViewport: () => {},
|
messageDidEnterViewport: () => {},
|
||||||
messageDidLeaveViewport: () => {},
|
messageDidLeaveViewport: () => {},
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user