Attachment Modal: Support hd photo (#5994)

This commit is contained in:
Alexander Zinchuk 2025-06-18 17:41:06 +02:00
parent a85fec6c65
commit c8360b2e5e
19 changed files with 278 additions and 202 deletions

View File

@ -36,6 +36,7 @@ export interface ApiOnProgress {
}
export interface ApiAttachment {
blob: Blob;
blobUrl: string;
compressedBlobUrl?: string;
filename: string;
@ -62,6 +63,7 @@ export interface ApiAttachment {
uniqueId?: string;
ttlSeconds?: number;
shouldSendInHighQuality?: boolean;
}
export interface ApiWallpaper {

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="M27.33 2.468A4.924 4.924 0 0 1 32 7.385v17.23l-.006.254a4.924 4.924 0 0 1-4.664 4.663l-.253.006H4.923l-.253-.006A4.925 4.925 0 0 1 .006 24.87L0 24.615V7.385a4.924 4.924 0 0 1 4.67-4.917l.253-.006h22.154zM4.923 4.923a2.46 2.46 0 0 0-2.461 2.462v17.23a2.46 2.46 0 0 0 2.46 2.462h22.155a2.46 2.46 0 0 0 2.461-2.462V7.385a2.46 2.46 0 0 0-2.46-2.462zm16 3.692a6.155 6.155 0 0 1 6.154 6.155V16l-.009.316a6.154 6.154 0 0 1-6.145 5.838H17.23V8.615zm-6.154 0c.68 0 1.23.551 1.23 1.23v11.078a1.23 1.23 0 1 1-2.46 0V17.23H7.383v3.692a1.23 1.23 0 0 1-2.462 0V9.846a1.231 1.231 0 0 1 2.462 0v4.924h6.154V9.846c0-.68.55-1.23 1.23-1.23m4.923 11.077h1.23A3.69 3.69 0 0 0 24.616 16v-1.23a3.69 3.69 0 0 0-3.692-3.693h-1.23z"/></svg>

After

Width:  |  Height:  |  Size: 810 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="M27.33 2.467A4.924 4.924 0 0 1 32 7.384v17.232l-.006.253a4.925 4.925 0 0 1-4.664 4.664l-.253.006H4.923l-.253-.006a4.925 4.925 0 0 1-4.664-4.664L0 24.616V7.384a4.924 4.924 0 0 1 4.67-4.917l.253-.006h22.154zM4.923 4.923a2.46 2.46 0 0 0-2.461 2.461v17.232c0 1.359 1.102 2.46 2.46 2.46h22.155c1.36 0 2.46-1.101 2.461-2.46V7.384a2.46 2.46 0 0 0-2.46-2.46zm16 3.693a6.154 6.154 0 0 1 6.154 6.153V16l-.009.316a6.154 6.154 0 0 1-6.145 5.838H17.23V8.616zm-1.23 11.076h1.23A3.69 3.69 0 0 0 24.615 16V14.77a3.69 3.69 0 0 0-3.692-3.692h-1.23z"/><path fill="#000" d="M13.41 12.418c-.624 0-1.104-.523-1.504-1.002a2 2 0 0 0-.367-.335q-.675-.48-1.757-.479-.76 0-1.305.23-.544.229-.833.622t-.295.898q0 .42.19.728.198.308.532.525.334.21.74.354t.82.242l1.259.315q.76.176 1.461.479.709.3 1.266.76.564.459.891 1.108.328.65.328 1.52 0 1.181-.603 2.08-.603.89-1.744 1.396-1.134.498-2.747.498-1.566 0-2.72-.485-1.147-.486-1.797-1.416a3.7 3.7 0 0 1-.492-1.024c-.204-.66.372-1.245 1.062-1.245.636 0 1.115.536 1.479 1.058q.039.056.082.11.38.465.99.694.616.23 1.377.23.793-.001 1.39-.236.602-.243.944-.67.34-.432.347-1.009-.006-.525-.308-.865-.302-.348-.846-.577a8 8 0 0 0-1.259-.42l-1.527-.393q-1.659-.426-2.623-1.292-.957-.87-.957-2.314 0-1.186.643-2.078.649-.891 1.763-1.383 1.115-.499 2.524-.499 1.43 0 2.505.499 1.08.492 1.698 1.37.261.37.416.789c.236.64-.34 1.217-1.023 1.217"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -2017,6 +2017,8 @@
"ValueGiftSortByNumber" = "Number";
"ResellGiftsNoFound" = "No gifts found";
"ResellGiftsClearFilters" = "Clear Filters";
"SendInStandardQuality" = "Send In Standard Quality";
"SendInHighQuality" = "Send In High Quality";
"MonoforumBadge" = "DIRECT";
"MonoforumStatus" = "Channel messages";
"MonoforumComposerPlaceholder" = "Choose a message to reply";

View File

@ -635,7 +635,6 @@ const Composer: FC<OwnProps & StateProps> = ({
});
const {
shouldSuggestCompression,
shouldForceCompression,
shouldForceAsFile,
handleAppendFiles,
@ -655,6 +654,7 @@ const Composer: FC<OwnProps & StateProps> = ({
canSendDocuments,
insertNextText,
editedMessage: editingMessage,
shouldSendInHighQuality: attachmentSettings.shouldSendInHighQuality,
});
const [isBotKeyboardOpen, openBotKeyboard, closeBotKeyboard] = useFlag();
@ -1848,7 +1848,6 @@ const Composer: FC<OwnProps & StateProps> = ({
attachments={attachments}
getHtml={getHtml}
isReady={isReady}
shouldSuggestCompression={shouldSuggestCompression}
shouldForceCompression={shouldForceCompression}
shouldForceAsFile={shouldForceAsFile}
isForCurrentMessageList={isForCurrentMessageList}

View File

@ -3,6 +3,7 @@ import {
memo, useEffect,
useMemo,
} from '../../../lib/teact/teact';
import { getActions } from '../../../global';
import type { ApiAttachMenuPeerType, ApiMessage } from '../../../api/types';
import type { GlobalState } from '../../../global/types';
@ -55,7 +56,7 @@ export type OwnProps = {
peerType?: ApiAttachMenuPeerType;
shouldCollectDebugLogs?: boolean;
theme: ThemeKey;
onFileSelect: (files: File[], shouldSuggestCompression?: boolean) => void;
onFileSelect: (files: File[]) => void;
onPollCreate: NoneToVoidFunction;
onMenuOpen: NoneToVoidFunction;
onMenuClose: NoneToVoidFunction;
@ -89,6 +90,9 @@ const AttachMenu: FC<OwnProps> = ({
messageListType,
paidMessagesStars,
}) => {
const {
updateAttachmentSettings,
} = getActions();
const [isAttachMenuOpen, openAttachMenu, closeAttachMenu] = useFlag();
const [handleMouseEnter, handleMouseLeave, markMouseInside] = useMouseInside(isAttachMenuOpen, closeAttachMenu);
@ -126,29 +130,31 @@ const AttachMenu: FC<OwnProps> = ({
}
});
const handleFileSelect = useLastCallback((e: Event, shouldSuggestCompression?: boolean) => {
const handleFileSelect = useLastCallback((e: Event) => {
const { files } = e.target as HTMLInputElement;
const validatedFiles = validateFiles(files);
if (validatedFiles?.length) {
onFileSelect(validatedFiles, shouldSuggestCompression);
onFileSelect(validatedFiles);
}
});
const handleQuickSelect = useLastCallback(() => {
updateAttachmentSettings({ shouldCompress: true });
openSystemFilesDialog(
Array.from(canSendVideoAndPhoto ? CONTENT_TYPES_WITH_PREVIEW : (
canSendPhotos ? SUPPORTED_PHOTO_CONTENT_TYPES : SUPPORTED_VIDEO_CONTENT_TYPES
)).join(','),
(e) => handleFileSelect(e, true),
(e) => handleFileSelect(e),
);
});
const handleDocumentSelect = useLastCallback(() => {
updateAttachmentSettings({ shouldCompress: false });
openSystemFilesDialog(!canSendDocuments && canSendAudios
? Array.from(SUPPORTED_AUDIO_CONTENT_TYPES).join(',') : (
'*'
), (e) => handleFileSelect(e, false));
), (e) => handleFileSelect(e));
});
const handleSendLogs = useLastCallback(() => {

View File

@ -75,7 +75,6 @@ export type OwnProps = {
isReady: boolean;
isForMessage?: boolean;
shouldSchedule?: boolean;
shouldSuggestCompression?: boolean;
shouldForceCompression?: boolean;
shouldForceAsFile?: boolean;
isForCurrentMessageList?: boolean;
@ -113,6 +112,7 @@ type StateProps = {
const ATTACHMENT_MODAL_INPUT_ID = 'caption-input-text';
const DROP_LEAVE_TIMEOUT_MS = 150;
const MAX_LEFT_CHARS_TO_SHOW = 100;
const CLOSE_MENU_ANIMATION_DURATION = 200;
const AttachmentModal: FC<OwnProps & StateProps> = ({
chatId,
@ -134,7 +134,6 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
shouldSuggestCustomEmoji,
customEmojiForEmoji,
attachmentSettings,
shouldSuggestCompression,
shouldForceCompression,
shouldForceAsFile,
isForCurrentMessageList,
@ -176,14 +175,16 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
const [isSymbolMenuOpen, openSymbolMenu, closeSymbolMenu] = useFlag();
const [shouldSendCompressed, setShouldSendCompressed] = useState(
shouldSuggestCompression ?? attachmentSettings.shouldCompress,
);
const shouldSendCompressed = attachmentSettings.shouldCompress;
const isSendingCompressed = Boolean(
(shouldSendCompressed || shouldForceCompression || isInAlbum) && !shouldForceAsFile,
);
const [shouldSendGrouped, setShouldSendGrouped] = useState(attachmentSettings.shouldSendGrouped);
const isInvertedMedia = attachmentSettings.isInvertedMedia;
const [shouldSendInHighQuality, setShouldSendInHighQuality] = useState(
attachmentSettings.shouldSendInHighQuality,
);
const [renderingShouldSendInHighQuality, setRenderingShouldSendInHighQuality] = useState(shouldSendInHighQuality);
const {
handleScroll: handleAttachmentsScroll,
@ -197,6 +198,8 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
const renderingIsOpen = Boolean(renderingAttachments?.length);
const [isHovered, markHovered, unmarkHovered] = useFlag();
const timerRef = useRef<number | undefined>();
useEffect(() => {
if (!isOpen) {
closeSymbolMenu();
@ -269,16 +272,16 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
useEffect(() => {
if (isOpen) {
setShouldSendCompressed(shouldSuggestCompression ?? attachmentSettings.shouldCompress);
setShouldSendGrouped(attachmentSettings.shouldSendGrouped);
setShouldSendInHighQuality(attachmentSettings.shouldSendInHighQuality);
}
}, [attachmentSettings, isOpen, shouldSuggestCompression]);
}, [attachmentSettings, isOpen]);
useEffect(() => {
if (!isOpen) {
updateAttachmentSettings({ isInvertedMedia: undefined });
}
}, [updateAttachmentSettings, isOpen, shouldSuggestCompression]);
}, [updateAttachmentSettings, isOpen]);
function setIsInvertedMedia(value?: true) {
updateAttachmentSettings({ isInvertedMedia: value });
@ -303,9 +306,10 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
: isSilent ? onSendSilent : onSend;
send(isSendingCompressed, shouldSendGrouped, isInvertedMedia);
updateAttachmentSettings({
shouldCompress: shouldSuggestCompression === undefined ? isSendingCompressed : undefined,
shouldCompress: isSendingCompressed,
shouldSendGrouped,
isInvertedMedia,
shouldSendInHighQuality,
});
}
});
@ -387,6 +391,17 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
})));
});
const handleToggleShouldCompress = useLastCallback(() => {
const newValue = !shouldSendCompressed;
updateAttachmentSettings({ shouldCompress: newValue });
});
const handleToggleQuality = useLastCallback(() => {
const newValue = !shouldSendInHighQuality;
setShouldSendInHighQuality(newValue);
updateAttachmentSettings({ shouldSendInHighQuality: newValue });
});
const handleDisableSpoilers = useLastCallback(() => {
onAttachmentsUpdate(attachments.map((a) => ({ ...a, shouldSendAsSpoiler: undefined })));
});
@ -458,12 +473,13 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
const isQuickGallery = isSendingCompressed && hasOnlyMedia;
const [areAllPhotos, areAllVideos, areAllAudios] = useMemo(() => {
const [areAllPhotos, areAllVideos, areAllAudios, hasAnyPhoto] = useMemo(() => {
if (!isQuickGallery || !renderingAttachments) return [false, false, false];
const everyPhoto = renderingAttachments.every((a) => SUPPORTED_PHOTO_CONTENT_TYPES.has(a.mimeType));
const everyVideo = renderingAttachments.every((a) => SUPPORTED_VIDEO_CONTENT_TYPES.has(a.mimeType));
const everyAudio = renderingAttachments.every((a) => SUPPORTED_AUDIO_CONTENT_TYPES.has(a.mimeType));
return [everyPhoto, everyVideo, everyAudio];
const hasAnyPhoto = renderingAttachments.some((a) => SUPPORTED_PHOTO_CONTENT_TYPES.has(a.mimeType));
return [everyPhoto, everyVideo, everyAudio, hasAnyPhoto];
}, [renderingAttachments, isQuickGallery]);
const hasAnySpoilerable = useMemo(() => {
@ -471,6 +487,20 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
return renderingAttachments.some((a) => !SUPPORTED_AUDIO_CONTENT_TYPES.has(a.mimeType));
}, [renderingAttachments]);
useEffect(() => {
if (shouldSendInHighQuality === renderingShouldSendInHighQuality) return;
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = window.setTimeout(() => {
setRenderingShouldSendInHighQuality(shouldSendInHighQuality);
}, CLOSE_MENU_ANIMATION_DURATION);
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = undefined;
}
};
}, [shouldSendInHighQuality, renderingShouldSendInHighQuality]);
if (!renderingAttachments) {
return undefined;
}
@ -536,16 +566,24 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
{
!shouldForceAsFile && !shouldForceCompression && (isSendingCompressed ? (
<MenuItem icon="document" onClick={() => setShouldSendCompressed(false)}>
<MenuItem icon="document" onClick={handleToggleShouldCompress}>
{oldLang(isMultiple ? 'Attachment.SendAsFiles' : 'Attachment.SendAsFile')}
</MenuItem>
) : (
<MenuItem icon="photo" onClick={() => setShouldSendCompressed(true)}>
<MenuItem icon="photo" onClick={handleToggleShouldCompress}>
{isMultiple ? 'Send All as Media' : 'Send as Media'}
</MenuItem>
))
}
{isSendingCompressed && !editingMessage && hasAnyPhoto && (
<MenuItem
icon={renderingShouldSendInHighQuality ? 'sd-photo' : 'hd-photo'}
onClick={handleToggleQuality}
>
{lang(renderingShouldSendInHighQuality ? 'SendInStandardQuality' : 'SendInHighQuality')}
</MenuItem>
)}
{isSendingCompressed && hasAnySpoilerable && Boolean(!editingMessage) && (
hasSpoiler ? (
<MenuItem icon="spoiler-disable" onClick={handleDisableSpoilers}>

View File

@ -25,7 +25,7 @@ export type OwnProps = {
isOpen: boolean;
withQuick?: boolean;
onHide: NoneToVoidFunction;
onFileSelect: (files: File[], suggestCompression?: boolean) => void;
onFileSelect: (files: File[]) => void;
editingMessage?: ApiMessage | undefined;
};
@ -41,7 +41,7 @@ const DropArea: FC<OwnProps> = ({
isOpen, withQuick, onHide, onFileSelect, editingMessage,
}) => {
const lang = useLang();
const { showNotification } = getActions();
const { showNotification, updateAttachmentSettings } = getActions();
const hideTimeoutRef = useRef<number>();
const prevWithQuick = usePreviousDeprecated(withQuick);
const { shouldRender, transitionClassNames } = useShowTransitionDeprecated(isOpen);
@ -78,7 +78,8 @@ const DropArea: FC<OwnProps> = ({
}
onHide();
onFileSelect(files, withQuick ? false : undefined);
updateAttachmentSettings({ shouldCompress: withQuick ? false : undefined });
onFileSelect(files);
});
const handleQuickFilesDrop = useLastCallback(async (e: React.DragEvent<HTMLDivElement>) => {
@ -102,7 +103,8 @@ const DropArea: FC<OwnProps> = ({
}
onHide();
onFileSelect(files, true);
updateAttachmentSettings({ shouldCompress: true });
onFileSelect(files);
}
});

View File

@ -14,7 +14,8 @@ import {
} from '../../../../util/files';
import { scaleImage } from '../../../../util/imageResize';
const MAX_QUICK_IMG_SIZE = 1280; // px
const MAX_STANDARD_QUALITY_IMG_SIZE = 1280; // px
const MAX_HIGH_QUALITY_IMG_SIZE = 2560;
const MAX_THUMB_IMG_SIZE = 40; // px
const MAX_ASPECT_RATIO = 20;
const FILE_EXT_REGEX = /\.[^/.]+$/;
@ -28,22 +29,25 @@ export default async function buildAttachment(
let audio;
let previewBlobUrl;
let shouldSendAsFile;
const shouldSendInHighQuality = options?.shouldSendInHighQuality;
if (SUPPORTED_PHOTO_CONTENT_TYPES.has(mimeType)) {
const img = await preloadImage(blobUrl);
const { width, height } = img;
shouldSendAsFile = !validateAspectRatio(width, height);
const shouldShrink = Math.max(width, height) > MAX_QUICK_IMG_SIZE;
const maxQuickImgSize = shouldSendInHighQuality ? MAX_HIGH_QUALITY_IMG_SIZE : MAX_STANDARD_QUALITY_IMG_SIZE;
const shouldShrink = Math.max(width, height) > maxQuickImgSize;
const isGif = mimeType === GIF_MIME_TYPE;
if (!shouldSendAsFile) {
if (!options?.compressedBlobUrl && !isGif && (shouldShrink || mimeType !== 'image/jpeg')) {
const resizedUrl = await scaleImage(
blobUrl, shouldShrink ? MAX_QUICK_IMG_SIZE / Math.max(width, height) : 1, 'image/jpeg',
blobUrl, shouldShrink ? maxQuickImgSize / Math.max(width, height) : 1, 'image/jpeg',
);
URL.revokeObjectURL(blobUrl);
return buildAttachment(filename, blob, {
...options,
compressedBlobUrl: resizedUrl,
});
}
@ -88,6 +92,7 @@ export default async function buildAttachment(
}
return {
blob,
blobUrl,
filename,
mimeType,

View File

@ -1,4 +1,4 @@
import { useState } from '../../../../lib/teact/teact';
import { useEffect, useState } from '../../../../lib/teact/teact';
import { getActions } from '../../../../global';
import type { ApiAttachment, ApiMessage } from '../../../../api/types';
@ -22,6 +22,7 @@ export default function useAttachmentModal({
canSendDocuments,
insertNextText,
editedMessage,
shouldSendInHighQuality,
}: {
attachments: ApiAttachment[];
fileSizeLimit: number;
@ -34,12 +35,12 @@ export default function useAttachmentModal({
canSendDocuments?: boolean;
insertNextText: VoidFunction;
editedMessage: ApiMessage | undefined;
shouldSendInHighQuality?: boolean;
}) {
const lang = useLang();
const { openLimitReachedModal, showAllowedMessageTypesNotification, showNotification } = getActions();
const [shouldForceAsFile, setShouldForceAsFile] = useState<boolean>(false);
const [shouldForceCompression, setShouldForceCompression] = useState<boolean>(false);
const [shouldSuggestCompression, setShouldSuggestCompression] = useState<boolean | undefined>(undefined);
const handleClearAttachments = useLastCallback(() => {
setAttachments(MEMO_EMPTY_ARRAY);
@ -99,13 +100,14 @@ export default function useAttachmentModal({
}
} else {
const newAttachments = await Promise.all(files.map((file) => (
buildAttachment(file.name, file, { shouldSendAsSpoiler: isSpoiler || undefined })
buildAttachment(file.name, file,
{ shouldSendAsSpoiler: isSpoiler || undefined, shouldSendInHighQuality })
)));
handleSetAttachments([...attachments, ...newAttachments]);
}
});
const handleFileSelect = useLastCallback(async (files: File[], suggestCompression?: boolean) => {
const handleFileSelect = useLastCallback(async (files: File[]) => {
if (editedMessage) {
const newAttachment = await buildAttachment(files[0].name, files[0]);
const canReplace = editedMessage && canReplaceMessageMedia(editedMessage, newAttachment);
@ -120,14 +122,23 @@ export default function useAttachmentModal({
handleSetAttachments([newAttachment]);
}
} else {
const newAttachments = await Promise.all(files.map((file) => buildAttachment(file.name, file)));
const newAttachments = await Promise.all(files.map((file) =>
buildAttachment(file.name, file, { shouldSendInHighQuality })));
handleSetAttachments(newAttachments);
}
setShouldSuggestCompression(suggestCompression);
});
const handleUpdateAttachmentsQuality = useLastCallback(async () => {
const newAttachments = await Promise.all(attachments.map((attachment) =>
buildAttachment(attachment.filename, attachment.blob, { shouldSendInHighQuality })));
handleSetAttachments(newAttachments);
});
useEffect(() => {
handleUpdateAttachmentsQuality();
}, [shouldSendInHighQuality]);
return {
shouldSuggestCompression,
handleAppendFiles,
handleFileSelect,
onCaptionUpdate: setHtml,

View File

@ -529,17 +529,11 @@ addActionHandler('requestWave', (global, actions, payload): ActionReturnType =>
});
addActionHandler('updateAttachmentSettings', (global, actions, payload): ActionReturnType => {
const {
shouldCompress, shouldSendGrouped, isInvertedMedia, webPageMediaSize,
} = payload;
return {
...global,
attachmentSettings: {
shouldCompress: shouldCompress ?? global.attachmentSettings.shouldCompress,
shouldSendGrouped: shouldSendGrouped ?? global.attachmentSettings.shouldSendGrouped,
isInvertedMedia,
webPageMediaSize,
...global.attachmentSettings,
...payload,
},
};
});

View File

@ -180,6 +180,7 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
shouldSendGrouped: true,
isInvertedMedia: undefined,
webPageMediaSize: undefined,
shouldSendInHighQuality: false,
},
scheduledMessages: {

View File

@ -2193,6 +2193,7 @@ export interface ActionPayloads {
shouldSendGrouped?: boolean;
isInvertedMedia?: true;
webPageMediaSize?: WebPageMediaSize;
shouldSendInHighQuality?: boolean;
};
saveEffectInDraft: {

View File

@ -120,6 +120,7 @@ export type GlobalState = {
shouldSendGrouped: boolean;
isInvertedMedia?: true;
webPageMediaSize?: WebPageMediaSize;
shouldSendInHighQuality?: boolean;
};
attachMenu: {

View File

@ -140,166 +140,168 @@ $icons-map: (
"grouped": "\f167",
"hand-stop": "\f168",
"hashtag": "\f169",
"heart-outline": "\f16a",
"heart": "\f16b",
"help": "\f16c",
"info-filled": "\f16d",
"info": "\f16e",
"install": "\f16f",
"italic": "\f170",
"key": "\f171",
"keyboard": "\f172",
"lamp": "\f173",
"language": "\f174",
"large-pause": "\f175",
"large-play": "\f176",
"link-badge": "\f177",
"link-broken": "\f178",
"link": "\f179",
"location": "\f17a",
"lock-badge": "\f17b",
"lock": "\f17c",
"logout": "\f17d",
"loop": "\f17e",
"mention": "\f17f",
"message-failed": "\f180",
"message-pending": "\f181",
"message-read": "\f182",
"message-succeeded": "\f183",
"message": "\f184",
"microphone-alt": "\f185",
"microphone": "\f186",
"monospace": "\f187",
"more-circle": "\f188",
"more": "\f189",
"move-caption-down": "\f18a",
"move-caption-up": "\f18b",
"mute": "\f18c",
"muted": "\f18d",
"my-notes": "\f18e",
"new-chat-filled": "\f18f",
"next": "\f190",
"nochannel": "\f191",
"noise-suppression": "\f192",
"non-contacts": "\f193",
"one-filled": "\f194",
"open-in-new-tab": "\f195",
"password-off": "\f196",
"pause": "\f197",
"permissions": "\f198",
"phone-discard-outline": "\f199",
"phone-discard": "\f19a",
"phone": "\f19b",
"photo": "\f19c",
"pin-badge": "\f19d",
"pin-list": "\f19e",
"pin": "\f19f",
"pinned-chat": "\f1a0",
"pinned-message": "\f1a1",
"pip": "\f1a2",
"play-story": "\f1a3",
"play": "\f1a4",
"poll": "\f1a5",
"previous": "\f1a6",
"privacy-policy": "\f1a7",
"proof-of-ownership": "\f1a8",
"quote-text": "\f1a9",
"quote": "\f1aa",
"radial-badge": "\f1ab",
"readchats": "\f1ac",
"recent": "\f1ad",
"reload": "\f1ae",
"remove-quote": "\f1af",
"remove": "\f1b0",
"reopen-topic": "\f1b1",
"replace": "\f1b2",
"replies": "\f1b3",
"reply-filled": "\f1b4",
"reply": "\f1b5",
"revenue-split": "\f1b6",
"revote": "\f1b7",
"save-story": "\f1b8",
"saved-messages": "\f1b9",
"schedule": "\f1ba",
"search": "\f1bb",
"select": "\f1bc",
"sell-outline": "\f1bd",
"sell": "\f1be",
"send-outline": "\f1bf",
"send": "\f1c0",
"settings-filled": "\f1c1",
"settings": "\f1c2",
"share-filled": "\f1c3",
"share-screen-outlined": "\f1c4",
"share-screen-stop": "\f1c5",
"share-screen": "\f1c6",
"show-message": "\f1c7",
"sidebar": "\f1c8",
"skip-next": "\f1c9",
"skip-previous": "\f1ca",
"smallscreen": "\f1cb",
"smile": "\f1cc",
"sort-by-date": "\f1cd",
"sort-by-number": "\f1ce",
"sort-by-price": "\f1cf",
"sort": "\f1d0",
"speaker-muted-story": "\f1d1",
"speaker-outline": "\f1d2",
"speaker-story": "\f1d3",
"speaker": "\f1d4",
"spoiler-disable": "\f1d5",
"spoiler": "\f1d6",
"sport": "\f1d7",
"star": "\f1d8",
"stars-lock": "\f1d9",
"stats": "\f1da",
"stealth-future": "\f1db",
"stealth-past": "\f1dc",
"stickers": "\f1dd",
"stop-raising-hand": "\f1de",
"stop": "\f1df",
"story-caption": "\f1e0",
"story-expired": "\f1e1",
"story-priority": "\f1e2",
"story-reply": "\f1e3",
"strikethrough": "\f1e4",
"tag-add": "\f1e5",
"tag-crossed": "\f1e6",
"tag-filter": "\f1e7",
"tag-name": "\f1e8",
"tag": "\f1e9",
"timer": "\f1ea",
"toncoin": "\f1eb",
"trade": "\f1ec",
"transcribe": "\f1ed",
"truck": "\f1ee",
"unarchive": "\f1ef",
"underlined": "\f1f0",
"unique-profile": "\f1f1",
"unlist-outline": "\f1f2",
"unlist": "\f1f3",
"unlock-badge": "\f1f4",
"unlock": "\f1f5",
"unmute": "\f1f6",
"unpin": "\f1f7",
"unread": "\f1f8",
"up": "\f1f9",
"user-filled": "\f1fa",
"user-online": "\f1fb",
"user": "\f1fc",
"video-outlined": "\f1fd",
"video-stop": "\f1fe",
"video": "\f1ff",
"view-once": "\f200",
"voice-chat": "\f201",
"volume-1": "\f202",
"volume-2": "\f203",
"volume-3": "\f204",
"web": "\f205",
"webapp": "\f206",
"word-wrap": "\f207",
"zoom-in": "\f208",
"zoom-out": "\f209",
"hd-photo": "\f16a",
"heart-outline": "\f16b",
"heart": "\f16c",
"help": "\f16d",
"info-filled": "\f16e",
"info": "\f16f",
"install": "\f170",
"italic": "\f171",
"key": "\f172",
"keyboard": "\f173",
"lamp": "\f174",
"language": "\f175",
"large-pause": "\f176",
"large-play": "\f177",
"link-badge": "\f178",
"link-broken": "\f179",
"link": "\f17a",
"location": "\f17b",
"lock-badge": "\f17c",
"lock": "\f17d",
"logout": "\f17e",
"loop": "\f17f",
"mention": "\f180",
"message-failed": "\f181",
"message-pending": "\f182",
"message-read": "\f183",
"message-succeeded": "\f184",
"message": "\f185",
"microphone-alt": "\f186",
"microphone": "\f187",
"monospace": "\f188",
"more-circle": "\f189",
"more": "\f18a",
"move-caption-down": "\f18b",
"move-caption-up": "\f18c",
"mute": "\f18d",
"muted": "\f18e",
"my-notes": "\f18f",
"new-chat-filled": "\f190",
"next": "\f191",
"nochannel": "\f192",
"noise-suppression": "\f193",
"non-contacts": "\f194",
"one-filled": "\f195",
"open-in-new-tab": "\f196",
"password-off": "\f197",
"pause": "\f198",
"permissions": "\f199",
"phone-discard-outline": "\f19a",
"phone-discard": "\f19b",
"phone": "\f19c",
"photo": "\f19d",
"pin-badge": "\f19e",
"pin-list": "\f19f",
"pin": "\f1a0",
"pinned-chat": "\f1a1",
"pinned-message": "\f1a2",
"pip": "\f1a3",
"play-story": "\f1a4",
"play": "\f1a5",
"poll": "\f1a6",
"previous": "\f1a7",
"privacy-policy": "\f1a8",
"proof-of-ownership": "\f1a9",
"quote-text": "\f1aa",
"quote": "\f1ab",
"radial-badge": "\f1ac",
"readchats": "\f1ad",
"recent": "\f1ae",
"reload": "\f1af",
"remove-quote": "\f1b0",
"remove": "\f1b1",
"reopen-topic": "\f1b2",
"replace": "\f1b3",
"replies": "\f1b4",
"reply-filled": "\f1b5",
"reply": "\f1b6",
"revenue-split": "\f1b7",
"revote": "\f1b8",
"save-story": "\f1b9",
"saved-messages": "\f1ba",
"schedule": "\f1bb",
"sd-photo": "\f1bc",
"search": "\f1bd",
"select": "\f1be",
"sell-outline": "\f1bf",
"sell": "\f1c0",
"send-outline": "\f1c1",
"send": "\f1c2",
"settings-filled": "\f1c3",
"settings": "\f1c4",
"share-filled": "\f1c5",
"share-screen-outlined": "\f1c6",
"share-screen-stop": "\f1c7",
"share-screen": "\f1c8",
"show-message": "\f1c9",
"sidebar": "\f1ca",
"skip-next": "\f1cb",
"skip-previous": "\f1cc",
"smallscreen": "\f1cd",
"smile": "\f1ce",
"sort-by-date": "\f1cf",
"sort-by-number": "\f1d0",
"sort-by-price": "\f1d1",
"sort": "\f1d2",
"speaker-muted-story": "\f1d3",
"speaker-outline": "\f1d4",
"speaker-story": "\f1d5",
"speaker": "\f1d6",
"spoiler-disable": "\f1d7",
"spoiler": "\f1d8",
"sport": "\f1d9",
"star": "\f1da",
"stars-lock": "\f1db",
"stats": "\f1dc",
"stealth-future": "\f1dd",
"stealth-past": "\f1de",
"stickers": "\f1df",
"stop-raising-hand": "\f1e0",
"stop": "\f1e1",
"story-caption": "\f1e2",
"story-expired": "\f1e3",
"story-priority": "\f1e4",
"story-reply": "\f1e5",
"strikethrough": "\f1e6",
"tag-add": "\f1e7",
"tag-crossed": "\f1e8",
"tag-filter": "\f1e9",
"tag-name": "\f1ea",
"tag": "\f1eb",
"timer": "\f1ec",
"toncoin": "\f1ed",
"trade": "\f1ee",
"transcribe": "\f1ef",
"truck": "\f1f0",
"unarchive": "\f1f1",
"underlined": "\f1f2",
"unique-profile": "\f1f3",
"unlist-outline": "\f1f4",
"unlist": "\f1f5",
"unlock-badge": "\f1f6",
"unlock": "\f1f7",
"unmute": "\f1f8",
"unpin": "\f1f9",
"unread": "\f1fa",
"up": "\f1fb",
"user-filled": "\f1fc",
"user-online": "\f1fd",
"user": "\f1fe",
"video-outlined": "\f1ff",
"video-stop": "\f200",
"video": "\f201",
"view-once": "\f202",
"voice-chat": "\f203",
"volume-1": "\f204",
"volume-2": "\f205",
"volume-3": "\f206",
"web": "\f207",
"webapp": "\f208",
"word-wrap": "\f209",
"zoom-in": "\f20a",
"zoom-out": "\f20b",
);
.icon-active-sessions::before {
@ -617,6 +619,9 @@ $icons-map: (
.icon-hashtag::before {
content: map.get($icons-map, "hashtag");
}
.icon-hd-photo::before {
content: map.get($icons-map, "hd-photo");
}
.icon-heart-outline::before {
content: map.get($icons-map, "heart-outline");
}
@ -860,6 +865,9 @@ $icons-map: (
.icon-schedule::before {
content: map.get($icons-map, "schedule");
}
.icon-sd-photo::before {
content: map.get($icons-map, "sd-photo");
}
.icon-search::before {
content: map.get($icons-map, "search");
}

Binary file not shown.

Binary file not shown.

View File

@ -104,6 +104,7 @@ export type FontIconName =
| 'grouped'
| 'hand-stop'
| 'hashtag'
| 'hd-photo'
| 'heart-outline'
| 'heart'
| 'help'
@ -185,6 +186,7 @@ export type FontIconName =
| 'save-story'
| 'saved-messages'
| 'schedule'
| 'sd-photo'
| 'search'
| 'select'
| 'sell-outline'

View File

@ -1530,6 +1530,8 @@ export interface LangPair {
'ValueGiftSortByNumber': undefined;
'ResellGiftsNoFound': undefined;
'ResellGiftsClearFilters': undefined;
'SendInStandardQuality': undefined;
'SendInHighQuality': undefined;
'MonoforumBadge': undefined;
'MonoforumStatus': undefined;
'MonoforumComposerPlaceholder': undefined;