From 0ac730a043c56248ecb21781021f90f65e71a99e Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:27:48 +0100 Subject: [PATCH] Link: Fix opening forum topics (#5558) --- src/global/actions/api/chats.ts | 97 +++++++++++++++++++++++++++++++-- src/global/actions/ui/chats.ts | 16 ------ src/global/types/actions.ts | 3 + src/util/deepLinkParser.ts | 22 +++----- src/util/deeplink.ts | 24 +++----- 5 files changed, 109 insertions(+), 53 deletions(-) diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index 2c0bcb8bd..0622a3a8a 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -1577,6 +1577,43 @@ addActionHandler('openChatByUsername', async (global, actions, payload): Promise } }); +addActionHandler('openPrivateChannel', (global, actions, payload): ActionReturnType => { + const { + id, commentId, messageId, threadId, tabId = getCurrentTabId(), + } = payload; + const chat = selectChat(global, id); + if (!chat) { + actions.showNotification({ + message: { + key: 'PrivateChannelInaccessible', + }, + tabId, + }); + return; + } + + if (!commentId && !messageId && !threadId) { + actions.openChat({ id, tabId }); + return; + } + + if (commentId && messageId) { + actions.openThread({ + isComments: true, + originChannelId: chat.id, + originMessageId: messageId, + tabId, + focusMessageId: commentId, + }); + return; + } + + openChatWithParams(global, actions, chat, { + messageId, + threadId, + }, tabId); +}); + addActionHandler('togglePreHistoryHidden', async (global, actions, payload): Promise => { const { chatId, isEnabled, @@ -3075,7 +3112,6 @@ async function openChatByUsername( const { username, threadId, channelPostId, startParam, ref, startAttach, attach, text, } = params; - global = getGlobal(); const currentChat = selectCurrentChat(global, tabId); // Attach in the current chat @@ -3120,10 +3156,61 @@ async function openChatByUsername( return; } - if (channelPostId) { - actions.focusMessage({ - chatId: chat.id, threadId, messageId: channelPostId, tabId, - }); + openChatWithParams(global, actions, chat, { + isCurrentChat, + threadId, + messageId: channelPostId, + startParam, + referrer, + startAttach, + attach, + text, + }, tabId); +} + +async function openChatWithParams( + global: T, + actions: RequiredGlobalActions, + chat: ApiChat, + params: { + isCurrentChat?: boolean; + threadId?: ThreadId; + messageId?: number; + startParam?: string; + referrer?: string; + startAttach?: string; + attach?: string; + text?: string; + }, + ...[tabId = getCurrentTabId()]: TabArgs +) { + const { + isCurrentChat, threadId, messageId, startParam, referrer, startAttach, attach, text, + } = params; + + if (messageId) { + let isTopicProcessed = false; + // In forums, link to a topic start message should open the topic + if (chat.isForum && !threadId) { + let topic = selectTopics(global, chat.id)?.[messageId]; + if (!topic) { + const topicResult = await callApi('fetchTopicById', { chat, topicId: messageId }); + topic = topicResult?.topic; + } + + if (topic) { + actions.openThread({ + chatId: chat.id, threadId: topic.id, tabId, + }); + isTopicProcessed = true; + } + } + + if (!isTopicProcessed) { + actions.focusMessage({ + chatId: chat.id, threadId, messageId, tabId, + }); + } } else if (!isCurrentChat) { actions.openThread({ chatId: chat.id, threadId: threadId ?? MAIN_THREAD_ID, tabId }); } diff --git a/src/global/actions/ui/chats.ts b/src/global/actions/ui/chats.ts index 4174466a2..5e2e13469 100644 --- a/src/global/actions/ui/chats.ts +++ b/src/global/actions/ui/chats.ts @@ -88,22 +88,6 @@ addActionHandler('processOpenChatOrThread', (global, actions, payload): ActionRe return updateCurrentMessageList(global, chatId, threadId, type, shouldReplaceHistory, shouldReplaceLast, tabId); }); -addActionHandler('openPrivateChannel', (global, actions, payload): ActionReturnType => { - const { id, tabId = getCurrentTabId() } = payload; - const chat = selectChat(global, id); - if (!chat) { - actions.showNotification({ - message: { - key: 'PrivateChannelInaccessible', - }, - tabId, - }); - return; - } - - actions.openChat({ id, tabId }); -}); - addActionHandler('openChatInNewTab', (global, actions, payload): ActionReturnType => { const { chatId, threadId = MAIN_THREAD_ID } = payload; diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts index 61996b911..3a58fbf30 100644 --- a/src/global/types/actions.ts +++ b/src/global/types/actions.ts @@ -1089,6 +1089,9 @@ export interface ActionPayloads { } & WithTabId; openPrivateChannel: { id: string; + threadId?: ThreadId; + messageId?: number; + commentId?: number; } & WithTabId; loadFullChat: { chatId: string; diff --git a/src/util/deepLinkParser.ts b/src/util/deepLinkParser.ts index 27423f89d..9fd0e4cd0 100644 --- a/src/util/deepLinkParser.ts +++ b/src/util/deepLinkParser.ts @@ -18,7 +18,6 @@ interface PublicMessageLink { isSingle: boolean; threadId?: ThreadId; commentId?: number; - mediaTimestamp?: string; } export interface PrivateMessageLink { @@ -28,7 +27,6 @@ export interface PrivateMessageLink { isSingle: boolean; threadId?: ThreadId; commentId?: number; - mediaTimestamp?: string; } interface ShareLink { @@ -173,7 +171,7 @@ function parseTgLink(url: URL) { switch (deepLinkType) { case 'publicMessageLink': { const { - domain, post, single, thread, comment, t, + domain, post, single, thread, comment, } = queryParams; return buildPublicMessageLink({ username: domain, @@ -181,12 +179,11 @@ function parseTgLink(url: URL) { single, threadId: thread, commentId: comment, - mediaTimestamp: t, }); } case 'privateMessageLink': { const { - channel, post, single, thread, comment, t, + channel, post, single, thread, comment, } = queryParams; return buildPrivateMessageLink({ channelId: channel, @@ -194,7 +191,6 @@ function parseTgLink(url: URL) { single, threadId: thread, commentId: comment, - mediaTimestamp: t, }); } case 'shareLink': @@ -255,7 +251,7 @@ function parseHttpLink(url: URL) { switch (deepLinkType) { case 'publicMessageLink': { const { - single, comment, t, + single, comment, } = queryParams; const { username, @@ -276,12 +272,11 @@ function parseHttpLink(url: URL) { single, threadId: thread, commentId: comment, - mediaTimestamp: t, }); } case 'privateMessageLink': { const { - single, comment, t, + single, comment, } = queryParams; const { channelId, @@ -302,7 +297,6 @@ function parseHttpLink(url: URL) { single, threadId: thread, commentId: comment, - mediaTimestamp: t, }); } case 'shareLink': { @@ -463,7 +457,7 @@ function buildShareLink(params: BuilderParams): BuilderReturnType { const { - messageId, threadId, commentId, username, single, mediaTimestamp, + messageId, threadId, commentId, username, single, } = params; if (!username || !isUsernameValid(username)) { return undefined; @@ -484,13 +478,12 @@ function buildPublicMessageLink(params: PublicMessageLinkBuilderParams): Builder isSingle: single === '', threadId: threadId ? Number(threadId) : undefined, commentId: commentId ? Number(commentId) : undefined, - mediaTimestamp, }; } function buildPrivateMessageLink(params: PrivateMessageLinkBuilderParams): BuilderReturnType { const { - messageId, threadId, commentId, channelId, single, mediaTimestamp, + messageId, threadId, commentId, channelId, single, } = params; if (!channelId || !isNumber(channelId)) { return undefined; @@ -506,12 +499,11 @@ function buildPrivateMessageLink(params: PrivateMessageLinkBuilderParams): Build } return { type: 'privateMessageLink', - channelId, + channelId: toChannelId(channelId), messageId: Number(messageId), isSingle: single === '', threadId: threadId ? Number(threadId) : undefined, commentId: commentId ? Number(commentId) : undefined, - mediaTimestamp, }; } diff --git a/src/util/deeplink.ts b/src/util/deeplink.ts index 4a1040f58..5f1cdaa67 100644 --- a/src/util/deeplink.ts +++ b/src/util/deeplink.ts @@ -1,10 +1,9 @@ import { getActions } from '../global'; import type { ApiChatType, ApiFormattedText } from '../api/types'; -import type { DeepLinkMethod, PrivateMessageLink } from './deepLinkParser'; +import type { DeepLinkMethod } from './deepLinkParser'; import { API_CHAT_TYPES, RE_TG_LINK } from '../config'; -import { toChannelId } from '../global/helpers'; import { tryParseDeepLink } from './deepLinkParser'; import { IS_BAD_URL_PARSER } from './windowEnvironment'; @@ -15,7 +14,12 @@ export const processDeepLink = (url: string): boolean => { if (parsedLink) { switch (parsedLink.type) { case 'privateMessageLink': - handlePrivateMessageLink(parsedLink, actions); + actions.openPrivateChannel({ + id: parsedLink.channelId, + threadId: parsedLink.threadId, + messageId: parsedLink.messageId, + commentId: parsedLink.commentId, + }); return true; case 'publicMessageLink': { actions.openChatByUsername({ @@ -223,20 +227,6 @@ export function formatShareText(url?: string, text?: string, title?: string): Ap }; } -function handlePrivateMessageLink(link: PrivateMessageLink, actions: ReturnType) { - const { - focusMessage, - } = actions; - const { - channelId, messageId, threadId, - } = link; - focusMessage({ - chatId: toChannelId(channelId), - threadId, - messageId, - }); -} - function parseChooseParameter(choose?: string) { if (!choose) return undefined; const types = choose.toLowerCase().split(' ');