From f8bcd328658da351e7b6e00851c3705dd5dc0d05 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 8 Mar 2024 12:48:52 +0100 Subject: [PATCH] WebPage: Render preview and audio document (#4345) --- src/api/gramjs/apiBuilders/messageContent.ts | 40 +++++++++++++++++++- src/api/types/messages.ts | 1 + src/components/common/Audio.tsx | 10 +++-- src/components/middle/message/Message.scss | 2 +- src/components/middle/message/Message.tsx | 1 + src/components/middle/message/WebPage.scss | 15 ++++---- src/components/middle/message/WebPage.tsx | 33 ++++++++++++++-- src/global/helpers/messageMedia.ts | 15 +++++--- 8 files changed, 96 insertions(+), 21 deletions(-) diff --git a/src/api/gramjs/apiBuilders/messageContent.ts b/src/api/gramjs/apiBuilders/messageContent.ts index db7d040bb..49cbb3e38 100644 --- a/src/api/gramjs/apiBuilders/messageContent.ts +++ b/src/api/gramjs/apiBuilders/messageContent.ts @@ -216,6 +216,39 @@ export function buildVideoFromDocument(document: GramJs.Document, isSpoiler?: bo }; } +export function buildAudioFromDocument(document: GramJs.Document): ApiAudio | undefined { + if (document instanceof GramJs.DocumentEmpty) { + return undefined; + } + + const { + id, mimeType, size, attributes, + } = document; + + const audioAttributes = attributes + .find((a: any): a is GramJs.DocumentAttributeAudio => a instanceof GramJs.DocumentAttributeAudio); + + if (!audioAttributes) { + return undefined; + } + + const { + duration, + title, + performer, + } = audioAttributes; + + return { + id: String(id), + mimeType, + duration, + fileName: getFilenameFromDocument(document, 'audio'), + title, + performer, + size: size.toJSNumber(), + }; +} + function buildVideo(media: GramJs.TypeMessageMedia): ApiVideo | undefined { if ( !(media instanceof GramJs.MessageMediaDocument) @@ -653,9 +686,13 @@ export function buildWebPage(media: GramJs.TypeMessageMedia): ApiWebPage | undef } = media.webpage; let video; + let audio; if (document instanceof GramJs.Document && document.mimeType.startsWith('video/')) { video = buildVideoFromDocument(document); } + if (document instanceof GramJs.Document && document.mimeType.startsWith('audio/')) { + audio = buildAudioFromDocument(document); + } let story: ApiWebPageStoryData | undefined; const attributeStory = attributes ?.find((a: any): a is GramJs.WebPageAttributeStory => a instanceof GramJs.WebPageAttributeStory); @@ -683,8 +720,9 @@ export function buildWebPage(media: GramJs.TypeMessageMedia): ApiWebPage | undef 'duration', ]), photo: photo instanceof GramJs.Photo ? buildApiPhoto(photo) : undefined, - document: !video && document ? buildApiDocument(document) : undefined, + document: !video && !audio && document ? buildApiDocument(document) : undefined, video, + audio, story, }; } diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts index 013a6a24b..de7130b72 100644 --- a/src/api/types/messages.ts +++ b/src/api/types/messages.ts @@ -329,6 +329,7 @@ export interface ApiWebPage { title?: string; description?: string; photo?: ApiPhoto; + audio?: ApiAudio; duration?: number; document?: ApiDocument; video?: ApiVideo; diff --git a/src/components/common/Audio.tsx b/src/components/common/Audio.tsx index ad09c9610..6565f4eee 100644 --- a/src/components/common/Audio.tsx +++ b/src/components/common/Audio.tsx @@ -16,6 +16,7 @@ import { getMediaTransferState, getMessageMediaFormat, getMessageMediaHash, + getMessageWebPageAudio, hasMessageTtl, isMessageLocal, isOwnMessage, @@ -67,7 +68,7 @@ type OwnProps = { isTranscriptionError?: boolean; autoPlay?: boolean; onHideTranscription?: (isHidden: boolean) => void; - onPlay: (messageId: number, chatId: string) => void; + onPlay?: (messageId: number, chatId: string) => void; onPause?: NoneToVoidFunction; onReadMedia?: () => void; onCancelUpload?: () => void; @@ -112,9 +113,10 @@ const Audio: FC = ({ const { content: { - audio, voice, video, + audio: contentAudio, voice, video, }, isMediaUnread, } = message; + const audio = contentAudio || getMessageWebPageAudio(message); const isVoice = Boolean(voice || video); const isSeeking = useRef(false); // eslint-disable-next-line no-null/no-null @@ -145,7 +147,7 @@ const Audio: FC = ({ const handleForcePlay = useLastCallback(() => { setIsActivated(true); - onPlay(message.id, message.chatId); + onPlay?.(message.id, message.chatId); }); const handleTrackChange = useLastCallback(() => { @@ -228,7 +230,7 @@ const Audio: FC = ({ } if (!isPlaying) { - onPlay(message.id, message.chatId); + onPlay?.(message.id, message.chatId); } getActions().setAudioPlayerOrigin({ origin }); diff --git a/src/components/middle/message/Message.scss b/src/components/middle/message/Message.scss index 2ca79dad5..462406822 100644 --- a/src/components/middle/message/Message.scss +++ b/src/components/middle/message/Message.scss @@ -642,7 +642,7 @@ } &.has-replies:not(.custom-shape), - &.text { + &.text:not(.web-page) { .media-inner, .Album { --border-bottom-left-radius: 0; diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index d1ffe66eb..6e9776f35 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -1228,6 +1228,7 @@ const Message: FC = ({ backgroundEmojiId={sender?.color?.backgroundEmojiId} shouldWarnAboutSvg={shouldWarnAboutSvg} autoLoadFileMaxSizeMb={autoLoadFileMaxSizeMb} + onAudioPlay={handleAudioPlay} onMediaClick={handleMediaClick} onCancelMediaTransfer={handleCancelUpload} /> diff --git a/src/components/middle/message/WebPage.scss b/src/components/middle/message/WebPage.scss index a4843c9de..9a8fb4c93 100644 --- a/src/components/middle/message/WebPage.scss +++ b/src/components/middle/message/WebPage.scss @@ -9,6 +9,10 @@ border-radius: 0.25rem; position: relative; overflow: hidden; + + &.with-video { + padding: 0.1875rem 0.375rem; + } &.with-document { --file-icon-border-color: var(--accent-background-color); @@ -77,13 +81,15 @@ .media-inner { margin: 0 !important; - margin-bottom: 0.375rem !important; &, & img, & canvas, &.small-image img { - border-radius: var(--border-radius-messages-small) !important; + --border-top-left-radius: var(--border-radius-messages-small); + --border-top-right-radius: var(--border-radius-messages-small); + --border-bottom-left-radius: var(--border-radius-messages-small); + --border-bottom-right-radius: var(--border-radius-messages-small); } &.square-image { @@ -100,11 +106,6 @@ } } - &.with-video .media-inner { - margin-top: 0.5rem !important; - margin-bottom: 1rem !important; - } - .message-content:not(.has-reactions) & { margin-bottom: calc(var(--message-meta-height)) !important; } diff --git a/src/components/middle/message/WebPage.tsx b/src/components/middle/message/WebPage.tsx index 524f31715..21acd1112 100644 --- a/src/components/middle/message/WebPage.tsx +++ b/src/components/middle/message/WebPage.tsx @@ -4,7 +4,7 @@ import { getActions } from '../../../global'; import type { ApiMessage, ApiTypeStory } from '../../../api/types'; import type { ObserveFn } from '../../../hooks/useIntersectionObserver'; -import type { ISettings } from '../../../types'; +import { AudioOrigin, type ISettings } from '../../../types'; import { getMessageWebPage } from '../../../global/helpers'; import buildClassName from '../../../util/buildClassName'; @@ -18,6 +18,7 @@ import useEnsureStory from '../../../hooks/useEnsureStory'; import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; +import Audio from '../../common/Audio'; import Document from '../../common/Document'; import EmojiIconBackground from '../../common/embedded/EmojiIconBackground'; import SafeLink from '../../common/SafeLink'; @@ -47,8 +48,9 @@ type OwnProps = { story?: ApiTypeStory; shouldWarnAboutSvg?: boolean; autoLoadFileMaxSizeMb?: number; - onMediaClick?: () => void; - onCancelMediaTransfer?: () => void; + onAudioPlay?: NoneToVoidFunction; + onMediaClick?: NoneToVoidFunction; + onCancelMediaTransfer?: NoneToVoidFunction; }; const WebPage: FC = ({ @@ -68,6 +70,7 @@ const WebPage: FC = ({ shouldWarnAboutSvg, autoLoadFileMaxSizeMb, onMediaClick, + onAudioPlay, onCancelMediaTransfer, }) => { const { openTelegramLink } = getActions(); @@ -103,6 +106,7 @@ const WebPage: FC = ({ description, photo, video, + audio, type, document, } = webPage; @@ -200,6 +204,17 @@ const WebPage: FC = ({ onCancelUpload={onCancelMediaTransfer} /> )} + {!inPreview && audio && ( +