FEATURE: adds a link to original message (#25503)

This commit adds a link to the original message of a thread, this link will:
- load the channel message and highlight it while keeping thread panel open on desktop
- open the channel and highlight the message in mobile (and close thread panel, as mobile never shows channel and thread in the same view)

Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
This commit is contained in:
Joffrey JAFFEUX 2024-02-01 18:27:38 +01:00 committed by GitHub
parent 34a83fe1c8
commit 550895a970
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 141 additions and 23 deletions

View File

@ -2,6 +2,9 @@ export default function () {
this.route("chat", function () {
this.route("channel", { path: "/c/:channelTitle/:channelId" }, function () {
this.route("near-message", { path: "/:messageId" });
this.route("near-message-with-thread", {
path: "/:messageId/t/:threadId",
});
this.route("threads", { path: "/t" });
this.route("thread", { path: "/t/:threadId" }, function () {
this.route("near-message", { path: "/:messageId" });

View File

@ -99,9 +99,7 @@ export default class ChatMessage extends Component {
}
get pane() {
return this.args.context === MESSAGE_CONTEXT_THREAD
? this.chatThreadPane
: this.chatChannelPane;
return this.threadContext ? this.chatThreadPane : this.chatChannelPane;
}
get messageInteractor() {
@ -459,7 +457,7 @@ export default class ChatMessage extends Component {
get hideReplyToInfo() {
return (
this.args.context === MESSAGE_CONTEXT_THREAD ||
this.threadContext ||
this.args.message?.inReplyTo?.id ===
this.args.message?.previousMessage?.id ||
this.threadingEnabled
@ -475,13 +473,17 @@ export default class ChatMessage extends Component {
get showThreadIndicator() {
return (
this.args.context !== MESSAGE_CONTEXT_THREAD &&
!this.threadContext &&
this.threadingEnabled &&
this.args.message?.thread &&
this.args.message?.thread.preview.replyCount > 0
);
}
get threadContext() {
return this.args.context === MESSAGE_CONTEXT_THREAD;
}
#teardownMentionedUsers() {
this.args.message.mentionedUsers.forEach((user) => {
user.stopTrackingStatus();
@ -573,6 +575,7 @@ export default class ChatMessage extends Component {
<ChatMessageInfo
@message={{@message}}
@show={{not this.hideUserInfo}}
@threadContext={{this.threadContext}}
/>
<ChatMessageText

View File

@ -10,9 +10,12 @@ import { prioritizeNameInUx } from "discourse/lib/settings";
import dIcon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
import { bind } from "discourse-common/utils/decorators";
import and from "truth-helpers/helpers/and";
import ChannelTitle from "discourse/plugins/chat/discourse/components/channel-title";
import formatChatDate from "../../../helpers/format-chat-date";
export default class ChatMessageInfo extends Component {
@service site;
@service siteSettings;
@bind
@ -57,7 +60,9 @@ export default class ChatMessageInfo extends Component {
}
get isFlagged() {
return this.#message?.reviewableId || this.#message?.userFlagStatus === 0;
return (
this.args.message?.reviewableId || this.args.message?.userFlagStatus === 0
);
}
get prioritizeName() {
@ -72,11 +77,27 @@ export default class ChatMessageInfo extends Component {
}
get #user() {
return this.#message?.user;
return this.args.message?.user;
}
get #message() {
return this.args.message;
get routeModels() {
if (this.site.mobileView) {
return [...this.args.message.channel.routeModels, this.args.message.id];
} else {
return [
...this.args.message.channel.routeModels,
this.args.message.id,
this.args.message.thread.id,
];
}
}
get route() {
if (this.site.mobileView) {
return "chat.channel.near-message";
} else {
return "chat.channel.near-message-with-thread";
}
}
<template>
@ -141,6 +162,19 @@ export default class ChatMessageInfo extends Component {
{{/if}}
</span>
{{/if}}
{{#if (and @threadContext @message.isOriginalThreadMessage)}}
<LinkTo
@route={{this.route}}
@models={{this.routeModels}}
class="chat-message-info__original-message"
>
<span class="chat-message-info__original-message__text">
{{i18n "chat.see_in"}}
</span>
<ChannelTitle @channel={{@message.channel}} />
</LinkTo>
{{/if}}
</div>
{{/if}}
</template>

View File

@ -160,6 +160,10 @@ export default class ChatMessage {
return this.channel.currentUserMembership?.lastReadMessageId >= this.id;
}
get isOriginalThreadMessage() {
return this.thread?.originalMessage?.id === this.id;
}
@cached
get index() {
return this.manager?.messages?.indexOf(this);

View File

@ -0,0 +1,30 @@
import { inject as service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";
// This route is only here as a convenience method for a clean `/c/:channelTitle/:channelId/:messageId/t/:threadId` URL.
// It's not a real route, it just redirects to the real route after setting a param on the controller.
export default class ChatChannelNearMessageWithThread extends DiscourseRoute {
@service router;
@service site;
beforeModel() {
const channel = this.modelFor("chat-channel");
const { messageId, threadId } = this.paramsFor(this.routeName);
this.controllerFor("chat-channel").set("messageId", null);
if (
messageId ||
this.controllerFor("chat-channel").get("targetMessageId")
) {
this.controllerFor("chat-channel").set("targetMessageId", messageId);
}
if (threadId && this.site.desktopView) {
this.router.replaceWith(
"chat.channel.thread",
...channel.routeModels,
threadId
);
}
}
}

View File

@ -2,6 +2,7 @@
display: flex;
align-items: center;
justify-content: flex-start;
gap: 0.25rem;
}
.chat-message-info__username {
@ -10,7 +11,6 @@
& + .chat-message-info__bot-indicator,
& + .chat-message-info__date {
margin-left: 0.25em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -26,14 +26,10 @@
.chat-message-info__bot-indicator {
text-transform: uppercase;
padding: 0.25em;
padding: 0.25rem;
background: var(--primary-low);
border-radius: var(--d-border-radius);
font-size: var(--font-down-2);
& + .chat-message-info__date {
margin-left: 0.25em;
}
}
.chat-message-info__date {
@ -46,10 +42,6 @@
color: var(--primary);
}
}
& + .chat-message-info__flag {
margin-left: 0.25em;
}
}
.chat-message-info__flag {
@ -61,17 +53,49 @@
.d-icon-bookmark {
color: var(--primary-low-mid);
font-size: var(--font-down-2);
margin-left: 0.5em;
margin-left: 0.5rem;
}
}
.chat-message-info__status {
display: flex;
margin-left: 0.2em;
margin-right: 0.2em;
margin-left: 0.2rem;
margin-right: 0.2rem;
.emoji {
width: 16px;
height: 16px;
}
}
.chat-message-info__original-message {
display: flex;
align-items: center;
font-size: var(--font-down-2);
color: var(--primary-high);
gap: 0.5em;
line-height: 1.2;
background: var(--tertiary-very-low);
padding: 0.25em 0.75em;
border-radius: 8px;
.chat-channel-title {
gap: 0.25em;
}
.chat-channel-name {
color: var(--tertiary);
}
.chat-channel-icon {
.d-icon {
height: 0.9em;
width: 0.9em;
}
}
&__text,
&__text:visited {
color: var(--primary);
}
}

View File

@ -51,6 +51,7 @@ en:
cancel_reply: "Cancel reply"
chat_channels: "Channels"
browse_all_channels: "Browse all channels"
see_in: "See in"
move_to_channel:
title: "Move messages to channel"
instructions:

View File

@ -15,7 +15,7 @@ describe "Single thread in side panel", type: :system do
sign_in(current_user)
end
context "when threading_enabled is false for the channel" do
context "when threading is disabled for the channel" do
fab!(:channel) { Fabricate(:chat_channel) }
before { channel.update!(threading_enabled: false) }
@ -82,6 +82,17 @@ describe "Single thread in side panel", type: :system do
expect(chat_drawer_page).to have_open_channel(channel)
end
it "highlights the message in the channel when clicking original message link" do
chat_page.visit_thread(thread)
find(".chat-message-info__original-message").click
expect(channel_page.messages).to have_message(
id: thread.original_message.id,
highlighted: true,
)
end
it "opens the side panel for a single thread from the indicator" do
chat_page.visit_channel(channel)
channel_page.message_thread_indicator(thread.original_message).click
@ -194,6 +205,14 @@ describe "Single thread in side panel", type: :system do
expect(side_panel).to have_open_thread(thread)
end
it "navigates back to channel when clicking original message link", mobile: true do
chat_page.visit_thread(thread)
find(".chat-message-info__original-message").click
expect(page).to have_current_path("/chat/c/#{channel.slug}/#{channel.id}")
end
end
context "when messages are separated by a day" do