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:
parent
34a83fe1c8
commit
550895a970
|
@ -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" });
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue