mirror of
https://github.com/discourse/discourse.git
synced 2025-04-14 08:30:49 +08:00
FIX: improves linking of thread messages (#26095)
- The thread preview is now a regular link and can be right clicked - left gutter date, and regular date of a thread message will not correctly link to the thread's message
This commit is contained in:
parent
57df0d526e
commit
21a7ebf1bc
@ -1,146 +1,33 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
|
||||
import { service } from "@ember/service";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import formatDate from "discourse/helpers/format-date";
|
||||
import replaceEmoji from "discourse/helpers/replace-emoji";
|
||||
import htmlSafe from "discourse-common/helpers/html-safe";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import ChatThreadParticipants from "./chat-thread-participants";
|
||||
import ChatUserAvatar from "./chat-user-avatar";
|
||||
|
||||
export default class ChatMessageThreadIndicator extends Component {
|
||||
@service capabilities;
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
@service router;
|
||||
@service site;
|
||||
|
||||
@tracked isActive = false;
|
||||
|
||||
get interactiveUser() {
|
||||
return this.args.interactiveUser ?? true;
|
||||
}
|
||||
|
||||
@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("mousedown", this.openThread, {
|
||||
passive: true,
|
||||
});
|
||||
|
||||
this.element.addEventListener("keydown", 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("mousedown", this.openThread, {
|
||||
passive: true,
|
||||
});
|
||||
|
||||
this.element.removeEventListener("keydown", 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(event) {
|
||||
if (event?.type === "keydown" && event?.key !== "Enter") {
|
||||
return;
|
||||
}
|
||||
|
||||
// handle middle mouse
|
||||
if (
|
||||
event?.type === "mousedown" &&
|
||||
(event?.which === 2 || event?.shiftKey)
|
||||
) {
|
||||
window.open(
|
||||
getURL(
|
||||
this.router.urlFor(
|
||||
"chat.channel.thread",
|
||||
...this.args.message.thread.routeModels
|
||||
)
|
||||
),
|
||||
"_blank"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.chat.activeMessage = null;
|
||||
|
||||
this.router.transitionTo(
|
||||
"chat.channel.thread",
|
||||
...this.args.message.thread.routeModels
|
||||
);
|
||||
get threadMessageRoute() {
|
||||
return [
|
||||
...this.args.message.thread.routeModels,
|
||||
this.args.message.thread.preview.lastReplyId,
|
||||
];
|
||||
}
|
||||
|
||||
<template>
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-message-thread-indicator"
|
||||
(if this.isActive "-active")
|
||||
}}
|
||||
{{didInsert this.setup}}
|
||||
{{willDestroy this.teardown}}
|
||||
role="button"
|
||||
<LinkTo
|
||||
class="chat-message-thread-indicator"
|
||||
@route="chat.channel.thread.near-message"
|
||||
@models={{this.threadMessageRoute}}
|
||||
title={{i18n "chat.threads.open"}}
|
||||
tabindex="0"
|
||||
...attributes
|
||||
>
|
||||
|
||||
<div class="chat-message-thread-indicator__last-reply-avatar">
|
||||
<ChatUserAvatar
|
||||
@user={{@message.thread.preview.lastReplyUser}}
|
||||
@ -167,6 +54,6 @@ export default class ChatMessageThreadIndicator extends Component {
|
||||
<div class="chat-message-thread-indicator__last-reply-excerpt">
|
||||
{{replaceEmoji (htmlSafe @message.thread.preview.lastReplyExcerpt)}}
|
||||
</div>
|
||||
</div>
|
||||
</LinkTo>
|
||||
</template>
|
||||
}
|
||||
|
@ -589,7 +589,10 @@ export default class ChatMessage extends Component {
|
||||
{{/unless}}
|
||||
|
||||
{{#if this.hideUserInfo}}
|
||||
<ChatMessageLeftGutter @message={{@message}} />
|
||||
<ChatMessageLeftGutter
|
||||
@message={{@message}}
|
||||
@threadContext={{this.threadContext}}
|
||||
/>
|
||||
{{else}}
|
||||
<ChatMessageAvatar @message={{@message}} />
|
||||
{{/if}}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
@ -142,7 +143,7 @@ export default class ChatMessageInfo extends Component {
|
||||
{{/if}}
|
||||
|
||||
<span class="chat-message-info__date">
|
||||
{{formatChatDate @message}}
|
||||
{{formatChatDate @message (hash threadContext=@threadContext)}}
|
||||
</span>
|
||||
|
||||
{{#if @message.bookmark}}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { service } from "@ember/service";
|
||||
import { eq } from "truth-helpers";
|
||||
@ -25,7 +26,10 @@ export default class ChatMessageLeftGutter extends Component {
|
||||
</div>
|
||||
{{else if this.site.desktopView}}
|
||||
<span class="chat-message-left-gutter__date">
|
||||
{{formatChatDate @message "tiny"}}
|
||||
{{formatChatDate
|
||||
@message
|
||||
(hash mode="tiny" threadContext=@threadContext)
|
||||
}}
|
||||
</span>
|
||||
{{/if}}
|
||||
{{#if @message.bookmark}}
|
||||
|
@ -3,14 +3,14 @@ import User from "discourse/models/user";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export default function formatChatDate(message, mode) {
|
||||
export default function formatChatDate(message, options = {}) {
|
||||
const currentUser = User.current();
|
||||
const tz = currentUser ? currentUser.user_option.timezone : moment.tz.guess();
|
||||
const date = moment(new Date(message.createdAt), tz);
|
||||
|
||||
const title = date.format(I18n.t("dates.long_with_year"));
|
||||
const display =
|
||||
mode === "tiny"
|
||||
options.mode === "tiny"
|
||||
? date.format(I18n.t("chat.dates.time_tiny"))
|
||||
: date.format(I18n.t("dates.time"));
|
||||
|
||||
@ -19,7 +19,15 @@ export default function formatChatDate(message, mode) {
|
||||
`<span title='${title}' tabindex="-1" class='chat-time'>${display}</span>`
|
||||
);
|
||||
} else {
|
||||
const url = getURL(`/chat/c/-/${message.channel.id}/${message.id}`);
|
||||
let url;
|
||||
if (options.threadContext) {
|
||||
url = getURL(
|
||||
`/chat/c/-/${message.channel.id}/t/${message.thread.id}/${message.id}`
|
||||
);
|
||||
} else {
|
||||
url = getURL(`/chat/c/-/${message.channel.id}/${message.id}`);
|
||||
}
|
||||
|
||||
return htmlSafe(
|
||||
`<a title='${title}' tabindex="-1" class='chat-time' href='${url}'>${display}</a>`
|
||||
);
|
||||
|
@ -29,12 +29,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.touch & {
|
||||
&.-active {
|
||||
box-shadow: var(--shadow-dropdown);
|
||||
}
|
||||
}
|
||||
|
||||
.no-touch & {
|
||||
&:hover {
|
||||
box-shadow: var(--shadow-dropdown);
|
||||
|
@ -19,4 +19,19 @@ module("Discourse Chat | Unit | Helpers | format-chat-date", function (hooks) {
|
||||
`/chat/c/-/${channel.id}/${this.message.id}`
|
||||
);
|
||||
});
|
||||
|
||||
test("link to chat message thread", async function (assert) {
|
||||
const channel = fabricators.channel();
|
||||
const thread = fabricators.thread();
|
||||
this.message = fabricators.message({ channel, thread });
|
||||
|
||||
await render(
|
||||
hbs`{{format-chat-date this.message (hash threadContext=true)}}`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
query(".chat-time").getAttribute("href"),
|
||||
`/chat/c/-/${channel.id}/t/${thread.id}/${this.message.id}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user