Message: Generate sharing link on server (#4405)

This commit is contained in:
Alexander Zinchuk 2024-03-22 13:06:14 +01:00
parent b3e21e293a
commit c0e82bdd0f
9 changed files with 100 additions and 30 deletions

View File

@ -35,7 +35,7 @@ export {
reportMessages, sendMessageAction, fetchSeenBy, fetchSponsoredMessages, viewSponsoredMessage, fetchSendAs,
saveDefaultSendAs, fetchUnreadReactions, readAllReactions, fetchUnreadMentions, readAllMentions, transcribeAudio,
closePoll, fetchExtendedMedia, translateText, fetchMessageViews, fetchDiscussionMessage, clickSponsoredMessage,
fetchOutboxReadDate,
fetchOutboxReadDate, exportMessageLink,
deleteSavedHistory,
} from './messages';

View File

@ -1927,3 +1927,21 @@ export async function fetchOutboxReadDate({ chat, messageId }: { chat: ApiChat;
return { date: result.date };
}
export async function exportMessageLink({
id, chat, shouldIncludeThread, shouldIncludeGrouped,
}: {
id: number;
chat: ApiChat;
shouldIncludeThread?: boolean;
shouldIncludeGrouped?: boolean;
}) {
const result = await invokeRequest(new GramJs.channels.ExportMessageLink({
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
id,
thread: shouldIncludeThread || undefined,
grouped: shouldIncludeGrouped || undefined,
}));
return result?.link;
}

View File

@ -4,11 +4,18 @@ import React, {
} from '../../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../../global';
import type {
ApiAvailableReaction, ApiChatReactions, ApiMessage, ApiReaction, ApiStickerSet, ApiStickerSetInfo, ApiThreadInfo,
} from '../../../api/types';
import type { MessageListType, TabState } from '../../../global/types';
import type { IAlbum, IAnchorPosition } from '../../../types';
import type { IAlbum, IAnchorPosition, ThreadId } from '../../../types';
import {
type ApiAvailableReaction,
type ApiChatReactions,
type ApiMessage,
type ApiReaction,
type ApiStickerSet,
type ApiStickerSetInfo,
type ApiThreadInfo,
MAIN_THREAD_ID,
} from '../../../api/types';
import { PREVIEW_AVATAR_COUNT, SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
import {
@ -38,7 +45,6 @@ import {
selectIsPremiumPurchaseBlocked,
selectIsReactionPickerOpen,
selectMessageCustomEmojiSets,
selectMessageLink,
selectMessageTranslations,
selectRequestedChatTranslationLanguage,
selectRequestedMessageTranslationLanguage,
@ -77,6 +83,7 @@ export type OwnProps = {
};
type StateProps = {
threadId?: ThreadId;
availableReactions?: ApiAvailableReaction[];
topReactions?: ApiReaction[];
defaultTagReactions?: ApiReaction[];
@ -120,13 +127,13 @@ type StateProps = {
maxUniqueReactions?: number;
canPlayAnimatedEmojis?: boolean;
isReactionPickerOpen?: boolean;
messageLink?: string;
isInSavedMessages?: boolean;
};
const selection = window.getSelection();
const ContextMenuContainer: FC<OwnProps & StateProps> = ({
threadId,
availableReactions,
topReactions,
defaultTagReactions,
@ -178,7 +185,6 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
canShowOriginal,
canSelectLanguage,
isReactionPickerOpen,
messageLink,
isInSavedMessages,
onClose,
onCloseAnimationEnd,
@ -213,6 +219,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
openMessageReactionPicker,
openPremiumModal,
loadOutboxReadDate,
copyMessageLink,
} = getActions();
const lang = useLang();
@ -459,7 +466,12 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
});
const handleCopyLink = useLastCallback(() => {
copyTextToClipboard(messageLink!);
copyMessageLink({
chatId: message.chatId,
messageId: message.id,
shouldIncludeThread: threadId !== MAIN_THREAD_ID,
shouldIncludeGrouped: true, // TODO: Provide correct value when ability to target specific message is added
});
closeMenu();
});
@ -728,11 +740,11 @@ export default memo(withGlobal<OwnProps>(
: undefined;
const canTranslate = !hasTranslation && selectCanTranslateMessage(global, message, detectedLanguage);
const isChatTranslated = selectRequestedChatTranslationLanguage(global, message.chatId);
const messageLink = selectMessageLink(global, message.chatId, threadId, message.id);
const isInSavedMessages = selectIsChatWithSelf(global, message.chatId);
return {
threadId,
availableReactions,
topReactions,
defaultTagReactions: defaultTags,
@ -777,7 +789,6 @@ export default memo(withGlobal<OwnProps>(
isMessageTranslated: hasTranslation,
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
isReactionPickerOpen: selectIsReactionPickerOpen(global),
messageLink,
isInSavedMessages,
};
},

View File

@ -103,11 +103,7 @@ export function getMessageCopyOptions(
options.push({
label: 'lng_context_copy_message_link',
icon: 'link',
handler: () => {
onCopyLink();
afterEffect?.();
},
handler: onCopyLink,
});
}

View File

@ -33,6 +33,7 @@ import {
SUPPORTED_IMAGE_CONTENT_TYPES,
SUPPORTED_VIDEO_CONTENT_TYPES,
} from '../../../config';
import { copyTextToClipboard } from '../../../util/clipboard';
import { isDeepLink } from '../../../util/deepLinkParser';
import { ensureProtocol } from '../../../util/ensureProtocol';
import { getCurrentTabId } from '../../../util/establishMultitabRole';
@ -53,6 +54,7 @@ import {
getIsSavedDialog,
getUserFullName,
isChatChannel,
isChatSuperGroup,
isDeletedUser,
isMessageLocal,
isServiceNotificationMessage,
@ -1900,6 +1902,49 @@ addActionHandler('loadOutboxReadDate', async (global, actions, payload): Promise
}
});
addActionHandler('copyMessageLink', async (global, actions, payload): Promise<void> => {
const {
chatId, messageId, shouldIncludeThread, shouldIncludeGrouped, tabId = getCurrentTabId(),
} = payload;
const chat = selectChat(global, chatId);
if (!chat) {
actions.showNotification({
message: translate('ErrorOccurred'),
tabId,
});
return;
}
if (!isChatChannel(chat) && !isChatSuperGroup(chat)) {
actions.showNotification({
message: translate('lng_filters_link_private_error'),
tabId,
});
return;
}
const link = await callApi('exportMessageLink', {
chat,
id: messageId,
shouldIncludeThread,
shouldIncludeGrouped,
});
if (!link) {
actions.showNotification({
message: translate('ErrorOccurred'),
tabId,
});
return;
}
copyTextToClipboard(link);
actions.showNotification({
message: translate('LinkCopied'),
tabId,
});
});
function countSortedIds(ids: number[], from: number, to: number) {
let count = 0;

View File

@ -1414,29 +1414,21 @@ export function selectCanTranslateMessage<T extends GlobalState>(
&& !chatRequestedLanguage;
}
export function selectMessageLink<T extends GlobalState>(
global: T, chatId: string, threadId?: ThreadId, messageId?: number,
export function selectTopicLink<T extends GlobalState>(
global: T, chatId: string, topicId?: ThreadId,
) {
const chat = selectChat(global, chatId);
if (!chat) {
if (!chat || !chat?.isForum) {
return undefined;
}
const chatUsername = getMainUsername(chat);
const isChannelId = isChatChannel(chat) || isChatSuperGroup(chat);
const normalizedId = isChannelId ? chatId.replace('-100', '') : chatId.replace('-', '');
const normalizedId = chatId.replace('-100', '');
const chatPart = chatUsername || `c/${normalizedId}`;
const threadPart = threadId && threadId !== MAIN_THREAD_ID ? `/${threadId}` : '';
const messagePart = messageId ? `/${messageId}` : '';
return `${TME_LINK_PREFIX}${chatPart}${threadPart}${messagePart}`;
}
export function selectTopicLink<T extends GlobalState>(
global: T, chatId: string, topicId?: ThreadId,
) {
return selectMessageLink(global, chatId, topicId);
const topicPart = topicId && topicId !== MAIN_THREAD_ID ? `/${topicId}` : '';
return `${TME_LINK_PREFIX}${chatPart}${topicPart}`;
}
export function selectMessageReplyInfo<T extends GlobalState>(

View File

@ -2054,6 +2054,12 @@ export interface ActionPayloads {
markMentionsRead: {
messageIds: number[];
} & WithTabId;
copyMessageLink: {
chatId: string;
messageId: number;
shouldIncludeThread?: boolean;
shouldIncludeGrouped?: boolean;
} & WithTabId;
sendPollVote: {
chatId: string;

View File

@ -1455,6 +1455,7 @@ channels.joinChannel#24b524c5 channel:InputChannel = Updates;
channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.exportMessageLink#e63fadeb flags:# grouped:flags.0?true thread:flags.1?true channel:InputChannel id:int = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.editBanned#96e6cd81 channel:InputChannel participant:InputPeer banned_rights:ChatBannedRights = Updates;
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;

View File

@ -202,6 +202,7 @@
"channels.leaveChannel",
"channels.inviteToChannel",
"channels.deleteChannel",
"channels.exportMessageLink",
"channels.toggleSignatures",
"channels.editBanned",
"channels.readMessageContents",