From 02a5a2a44f65d1400b9ecad696ac70b1406aa8ab Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Thu, 5 Mar 2026 12:43:29 +0100 Subject: [PATCH] Implement Media Editor (#6658) Co-authored-by: Shahaf Antwarg --- src/assets/font-icons/brush.svg | 1 + src/assets/font-icons/crop.svg | 1 + src/assets/font-icons/flip.svg | 1 + src/assets/font-icons/redo.svg | 1 + src/assets/font-icons/rotate.svg | 1 + src/assets/font-icons/undo.svg | 1 + src/assets/localization/fallback.strings | 23 + src/components/common/Composer.tsx | 39 +- .../common/embedded/EmbeddedMessage.scss | 42 + .../common/embedded/EmbeddedMessage.tsx | 62 +- .../middle/composer/AttachmentModal.tsx | 57 +- .../middle/composer/AttachmentModalItem.tsx | 28 +- .../composer/ComposerEmbeddedMessage.tsx | 19 +- src/components/ui/mediaEditor/CropOverlay.tsx | 87 ++ src/components/ui/mediaEditor/CropPanel.tsx | 109 +++ src/components/ui/mediaEditor/DrawPanel.tsx | 220 +++++ .../ui/mediaEditor/DrawToolSvgs.tsx | 277 ++++++ .../ui/mediaEditor/MediaEditor.module.scss | 796 +++++++++++++++++ src/components/ui/mediaEditor/MediaEditor.tsx | 836 ++++++++++++++++++ .../ui/mediaEditor/RotationSlider.module.scss | 104 +++ .../ui/mediaEditor/RotationSlider.tsx | 84 ++ src/components/ui/mediaEditor/canvasUtils.ts | 226 +++++ .../ui/mediaEditor/hooks/useCanvasRenderer.ts | 155 ++++ .../ui/mediaEditor/hooks/useColorPicker.ts | 199 +++++ .../ui/mediaEditor/hooks/useCropper.ts | 474 ++++++++++ .../ui/mediaEditor/hooks/useDisplaySize.ts | 71 ++ .../ui/mediaEditor/hooks/useDrawing.ts | 112 +++ src/global/actions/ui/misc.ts | 27 +- src/global/helpers/messageMedia.ts | 4 + src/global/types/actions.ts | 3 + src/global/types/tabState.ts | 2 + src/styles/icons.css | 598 +++++++------ src/styles/icons.scss | 582 ++++++------ src/styles/icons.woff | Bin 40136 -> 40792 bytes src/styles/icons.woff2 | Bin 33456 -> 33984 bytes src/types/icons/font.ts | 24 +- src/types/language.d.ts | 23 + 37 files changed, 4666 insertions(+), 623 deletions(-) create mode 100644 src/assets/font-icons/brush.svg create mode 100644 src/assets/font-icons/crop.svg create mode 100644 src/assets/font-icons/flip.svg create mode 100644 src/assets/font-icons/redo.svg create mode 100644 src/assets/font-icons/rotate.svg create mode 100644 src/assets/font-icons/undo.svg create mode 100644 src/components/ui/mediaEditor/CropOverlay.tsx create mode 100644 src/components/ui/mediaEditor/CropPanel.tsx create mode 100644 src/components/ui/mediaEditor/DrawPanel.tsx create mode 100644 src/components/ui/mediaEditor/DrawToolSvgs.tsx create mode 100644 src/components/ui/mediaEditor/MediaEditor.module.scss create mode 100644 src/components/ui/mediaEditor/MediaEditor.tsx create mode 100644 src/components/ui/mediaEditor/RotationSlider.module.scss create mode 100644 src/components/ui/mediaEditor/RotationSlider.tsx create mode 100644 src/components/ui/mediaEditor/canvasUtils.ts create mode 100644 src/components/ui/mediaEditor/hooks/useCanvasRenderer.ts create mode 100644 src/components/ui/mediaEditor/hooks/useColorPicker.ts create mode 100644 src/components/ui/mediaEditor/hooks/useCropper.ts create mode 100644 src/components/ui/mediaEditor/hooks/useDisplaySize.ts create mode 100644 src/components/ui/mediaEditor/hooks/useDrawing.ts diff --git a/src/assets/font-icons/brush.svg b/src/assets/font-icons/brush.svg new file mode 100644 index 000000000..99adc1d2c --- /dev/null +++ b/src/assets/font-icons/brush.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/crop.svg b/src/assets/font-icons/crop.svg new file mode 100644 index 000000000..57d9a50ad --- /dev/null +++ b/src/assets/font-icons/crop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/flip.svg b/src/assets/font-icons/flip.svg new file mode 100644 index 000000000..e5a0d4f75 --- /dev/null +++ b/src/assets/font-icons/flip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/redo.svg b/src/assets/font-icons/redo.svg new file mode 100644 index 000000000..c18e2dcdc --- /dev/null +++ b/src/assets/font-icons/redo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/rotate.svg b/src/assets/font-icons/rotate.svg new file mode 100644 index 000000000..62df18363 --- /dev/null +++ b/src/assets/font-icons/rotate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/undo.svg b/src/assets/font-icons/undo.svg new file mode 100644 index 000000000..f01dc1cb0 --- /dev/null +++ b/src/assets/font-icons/undo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index a42a86f63..ae945568b 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -2413,6 +2413,29 @@ "GiftAuctionForSaleOnFragment" = "{count} for sale on Fragment >"; "GiftAuctionForSaleOnTelegram" = "{count} for sale on Telegram >"; "EmbeddedMessageNoCaption" = "Caption removed"; +"EditMedia" = "Edit Media"; +"Draw" = "Draw"; +"Crop" = "Crop"; +"Clear" = "Clear"; +"Undo" = "Undo"; +"Redo" = "Redo"; +"ResetCrop" = "Reset"; +"CustomColor" = "Custom Color"; +"Size" = "Size"; +"Tool" = "Tool"; +"Pen" = "Pen"; +"Arrow" = "Arrow"; +"Brush" = "Brush"; +"Neon" = "Neon"; +"Eraser" = "Eraser"; +"AspectRatio" = "Aspect ratio"; +"Free" = "Free"; +"Original" = "Original"; +"Square" = "Square"; +"HEX" = "HEX"; +"RGB" = "RGB"; +"Adjust" = "Adjust"; +"Text" = "Text"; "ConfirmBuyGiftForTonDescription" = "The seller only accepts TON as payment."; "TitleGiftLocked" = "Gift Locked"; "GiftLockedMessage" = "This gift is currently only available to earlier Telegram users. It will unlock for your account in about **{relativeDate}**."; diff --git a/src/components/common/Composer.tsx b/src/components/common/Composer.tsx index 04b93f4dd..8210e867a 100644 --- a/src/components/common/Composer.tsx +++ b/src/components/common/Composer.tsx @@ -1,4 +1,4 @@ -import type { FC, TeactNode } from '../../lib/teact/teact'; +import type { TeactNode } from '../../lib/teact/teact'; import { memo, useEffect, useMemo, useRef, useSignal, useState } from '../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../global'; @@ -39,7 +39,7 @@ import type { ThemeKey, ThreadId, } from '../../types'; -import { MAIN_THREAD_ID } from '../../api/types'; +import { ApiMediaFormat, MAIN_THREAD_ID } from '../../api/types'; import { BASE_EMOJI_KEYWORD_LANG, @@ -56,6 +56,10 @@ import { requestMeasure, requestNextMutation } from '../../lib/fasterdom/fasterd import { canEditMedia, getAllowedAttachmentOptions, + getMediaFilename, + getMediaHash, + getMessageDocumentPhoto, + getMessagePhoto, getReactionKey, getStoryKey, isChatAdmin, @@ -117,8 +121,10 @@ import { tryParseDeepLink } from '../../util/deepLinkParser'; import deleteLastCharacterOutsideSelection from '../../util/deleteLastCharacterOutsideSelection'; import { processMessageInputForCustomEmoji } from '../../util/emoji/customEmojiManager'; import { isUserId } from '../../util/entities/ids'; +import { fetchBlob } from '../../util/files'; import focusEditableElement from '../../util/focusEditableElement'; import { formatStarsAsIcon } from '../../util/localization/format'; +import { fetch } from '../../util/mediaLoader'; import { MEMO_EMPTY_ARRAY } from '../../util/memo'; import parseHtmlAsFormattedText from '../../util/parseHtmlAsFormattedText'; import { insertHtmlInSelection } from '../../util/selection'; @@ -311,6 +317,8 @@ type StateProps = { isAppConfigLoaded?: boolean; insertingPeerIdMention?: string; pollMaxAnswers?: number; + replyToMessage?: ApiMessage; + shouldOpenMessageMediaEditor?: TabState['shouldOpenMessageMediaEditor']; }; enum MainButtonState { @@ -335,7 +343,7 @@ const SELECT_MODE_TRANSITION_MS = 200; const SENDING_ANIMATION_DURATION = 350; const MOUNT_ANIMATION_DURATION = 430; -const Composer: FC = ({ +const Composer = ({ type, isOnActiveTab, dropAreaState, @@ -436,11 +444,13 @@ const Composer: FC = ({ isAppConfigLoaded, insertingPeerIdMention, pollMaxAnswers, + replyToMessage, + shouldOpenMessageMediaEditor, onDropHide, onFocus, onBlur, onForward, -}) => { +}: OwnProps & StateProps) => { const { sendMessage, clearDraft, @@ -695,6 +705,24 @@ const Composer: FC = ({ shouldSendInHighQuality: attachmentSettings.shouldSendInHighQuality, }); + const mediaEditRequestRef = useRef(Date.now()); + useEffect(() => { + if (!shouldOpenMessageMediaEditor) return; + const targetMessage = editingMessage || replyToMessage; + const media = targetMessage && (getMessagePhoto(targetMessage) || getMessageDocumentPhoto(targetMessage)); + if (!media) return; + const mediaHash = getMediaHash(media, 'full'); + if (!mediaHash) return; + const now = Date.now(); + mediaEditRequestRef.current = now; + fetch(mediaHash, ApiMediaFormat.BlobUrl).then(async (blobUrl) => { + if (mediaEditRequestRef.current !== now) return; + const blob = await fetchBlob(blobUrl); + const attachment = await buildAttachment(getMediaFilename(media), blob); + handleSetAttachments([attachment]); + }); + }, [editingMessage, replyToMessage, shouldOpenMessageMediaEditor, handleSetAttachments]); + const [isBotKeyboardOpen, openBotKeyboard, closeBotKeyboard] = useFlag(); const [isBotCommandMenuOpen, openBotCommandMenu, closeBotCommandMenu] = useFlag(); const [isSymbolMenuOpen, openSymbolMenu, closeSymbolMenu] = useFlag(); @@ -2563,6 +2591,7 @@ export default memo(withGlobal( const { language, shouldCollectDebugLogs } = selectSharedSettings(global); const { forwardMessages: { messageIds: forwardMessageIds }, + shouldOpenMessageMediaEditor, } = selectTabState(global); const baseEmojiKeywords = global.emojiKeywords[BASE_EMOJI_KEYWORD_LANG]; const emojiKeywords = language !== BASE_EMOJI_KEYWORD_LANG ? global.emojiKeywords[language] : undefined; @@ -2725,6 +2754,8 @@ export default memo(withGlobal( isAppConfigLoaded, insertingPeerIdMention, pollMaxAnswers: appConfig.pollMaxAnswers, + shouldOpenMessageMediaEditor, + replyToMessage, }; }, )(Composer)); diff --git a/src/components/common/embedded/EmbeddedMessage.scss b/src/components/common/embedded/EmbeddedMessage.scss index 39c786bb6..490b73190 100644 --- a/src/components/common/embedded/EmbeddedMessage.scss +++ b/src/components/common/embedded/EmbeddedMessage.scss @@ -242,6 +242,32 @@ &.round { border-radius: 1rem; } + + &.with-action-icon { + &::after { + content: ''; + + position: absolute; + top: 0; + left: 0; + + width: 100%; + height: 100%; + + opacity: 0; + background-color: rgba(0, 0, 0, 0.5); + + transition: opacity 0.15s; + } + + &:hover::after { + opacity: 1; + } + + &:hover .pictogram-action-icon { + opacity: 1; + } + } } .pictogram { @@ -250,6 +276,22 @@ object-fit: cover; } + .pictogram-action-icon { + pointer-events: none; + + position: absolute; + z-index: 1; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + color: white; + + opacity: 0; + + transition: opacity 0.15s; + } + &.inside-input { flex-grow: 1; margin: 0; diff --git a/src/components/common/embedded/EmbeddedMessage.tsx b/src/components/common/embedded/EmbeddedMessage.tsx index 7046ed07b..4a9eea144 100644 --- a/src/components/common/embedded/EmbeddedMessage.tsx +++ b/src/components/common/embedded/EmbeddedMessage.tsx @@ -1,5 +1,3 @@ -import type { FC } from '../../../lib/teact/teact'; -import type React from '../../../lib/teact/teact'; import { useMemo, useRef } from '../../../lib/teact/teact'; import type { @@ -31,7 +29,6 @@ import { renderTextWithEntities } from '../helpers/renderTextWithEntities'; import useMessageMediaHash from '../../../hooks/media/useMessageMediaHash'; import useThumbnail from '../../../hooks/media/useThumbnail'; -import { useFastClick } from '../../../hooks/useFastClick'; import { useIsIntersecting } from '../../../hooks/useIntersectionObserver'; import useLang from '../../../hooks/useLang'; import useMedia from '../../../hooks/useMedia'; @@ -65,15 +62,17 @@ type OwnProps = { isOpen?: boolean; isMediaNsfw?: boolean; noCaptions?: boolean; + pictogramActionIcon?: IconName; observeIntersectionForLoading?: ObserveFn; observeIntersectionForPlaying?: ObserveFn; onClick: ((e: React.MouseEvent) => void); + onPictogramClick?: ((e: React.MouseEvent) => void); }; const NBSP = '\u00A0'; const EMOJI_SIZE = 17; -const EmbeddedMessage: FC = ({ +const EmbeddedMessage = ({ className, message, replyInfo, @@ -91,10 +90,12 @@ const EmbeddedMessage: FC = ({ requestedChatTranslationLanguage, isMediaNsfw, noCaptions, + pictogramActionIcon, observeIntersectionForLoading, observeIntersectionForPlaying, onClick, -}) => { + onPictogramClick, +}: OwnProps) => { const ref = useRef(); const isIntersecting = useIsIntersecting(ref, observeIntersectionForLoading); @@ -144,8 +145,6 @@ const EmbeddedMessage: FC = ({ : message?.forwardInfo?.hiddenUserName; const areSendersSame = sender && sender.id === forwardSender?.id; - const { handleClick, handleMouseDown } = useFastClick(onClick); - function renderTextContent() { const isFree = !(suggestedPostInfo?.price?.amount); if (suggestedPostInfo) { @@ -316,14 +315,20 @@ const EmbeddedMessage: FC = ({ suggestedPostInfo && 'is-suggested-post', )} dir={lang.isRtl ? 'rtl' : undefined} - onClick={handleClick} - onMouseDown={handleMouseDown} + onClick={onClick} >
- {mediaThumbnail && renderPictogram( - mediaThumbnail, mediaBlobUrl, isVideoThumbnail, isRoundVideo, isProtected, isSpoiler, - )} + {mediaThumbnail && renderPictogram({ + thumbDataUri: mediaThumbnail, + blobUrl: mediaBlobUrl, + isFullVideo: isVideoThumbnail, + isRoundVideo, + isProtected, + isSpoiler, + pictogramActionIcon, + onPictogramClick, + })}

{renderTextContent()} @@ -337,21 +342,35 @@ const EmbeddedMessage: FC = ({ ); }; -function renderPictogram( - thumbDataUri: string, - blobUrl?: string, - isFullVideo?: boolean, - isRoundVideo?: boolean, - isProtected?: boolean, - isSpoiler?: boolean, -) { +function renderPictogram({ + thumbDataUri, + blobUrl, + isFullVideo, + isRoundVideo, + isProtected, + isSpoiler, + pictogramActionIcon, + onPictogramClick, +}: { + thumbDataUri: string; + blobUrl?: string; + isFullVideo?: boolean; + isRoundVideo?: boolean; + isProtected?: boolean; + isSpoiler?: boolean; + pictogramActionIcon?: IconName; + onPictogramClick?: ((e: React.MouseEvent) => void); +}) { const { width, height } = getPictogramDimensions(); const srcUrl = blobUrl || thumbDataUri; const shouldRenderVideo = isFullVideo && blobUrl; return ( -

+
{!isSpoiler && !shouldRenderVideo && ( {isProtected && } + {pictogramActionIcon && }
); } diff --git a/src/components/middle/composer/AttachmentModal.tsx b/src/components/middle/composer/AttachmentModal.tsx index cfc86a051..f3dd18081 100644 --- a/src/components/middle/composer/AttachmentModal.tsx +++ b/src/components/middle/composer/AttachmentModal.tsx @@ -24,6 +24,7 @@ import { validateFiles } from '../../../util/files'; import { formatStarsAsIcon } from '../../../util/localization/format'; import { removeAllSelections } from '../../../util/selection'; import { openSystemFilesDialog } from '../../../util/systemFilesDialog'; +import buildAttachment from './helpers/buildAttachment'; import getFilesFromDataTransferItems from './helpers/getFilesFromDataTransferItems'; import { getHtmlTextLength } from './helpers/getHtmlTextLength'; @@ -44,6 +45,7 @@ import useMentionTooltip from './hooks/useMentionTooltip'; import Button from '../../ui/Button'; import DropdownMenu from '../../ui/DropdownMenu'; +import MediaEditor from '../../ui/mediaEditor/MediaEditor'; import MenuItem from '../../ui/MenuItem'; import Modal from '../../ui/Modal'; import AttachmentModalItem from './AttachmentModalItem'; @@ -99,6 +101,7 @@ type StateProps = { captionLimit: number; attachmentSettings: GlobalState['attachmentSettings']; shouldSaveAttachmentsCompression?: boolean; + shouldOpenMessageMediaEditor?: boolean; }; const ATTACHMENT_MODAL_INPUT_ID = 'caption-input-text'; @@ -134,6 +137,7 @@ const AttachmentModal = ({ canScheduleUntilOnline, canSchedule, paidMessagesStars, + shouldOpenMessageMediaEditor, onAttachmentsUpdate, onCaptionUpdate, onSend, @@ -148,7 +152,9 @@ const AttachmentModal = ({ }: OwnProps & StateProps) => { const ref = useRef(); const svgRef = useRef(); - const { addRecentCustomEmoji, addRecentEmoji, updateAttachmentSettings } = getActions(); + const { + addRecentCustomEmoji, addRecentEmoji, updateAttachmentSettings, resetMessageMediaEditorRequest, + } = getActions(); const lang = useLang(); @@ -166,6 +172,16 @@ const AttachmentModal = ({ const notEditingFile = isEditingMessageFile !== 'file'; const [isSymbolMenuOpen, openSymbolMenu, closeSymbolMenu] = useFlag(); + const [editingAttachmentIndex, setEditingAttachmentIndex] = useState(undefined); + const editingAttachment = editingAttachmentIndex !== undefined + ? attachments[editingAttachmentIndex] : undefined; + + useEffect(() => { + if (shouldOpenMessageMediaEditor && attachments.length) { + setEditingAttachmentIndex(0); + resetMessageMediaEditorRequest(); + } + }, [shouldOpenMessageMediaEditor, attachments.length]); const shouldSendCompressed = attachmentSettings.shouldCompress; const isSendingCompressed = Boolean( @@ -413,6 +429,33 @@ const AttachmentModal = ({ })); }); + const handleEdit = useLastCallback((index: number) => { + setEditingAttachmentIndex(index); + }); + + const handleCloseEditor = useLastCallback(() => { + setEditingAttachmentIndex(undefined); + }); + + const handleSaveEdit = useLastCallback(async (file: File) => { + if (editingAttachmentIndex === undefined) return; + + const newAttachment = await buildAttachment(file.name, file, { + shouldSendAsFile: attachments[editingAttachmentIndex].shouldSendAsFile, + shouldSendAsSpoiler: attachments[editingAttachmentIndex].shouldSendAsSpoiler, + shouldSendInHighQuality: attachments[editingAttachmentIndex].shouldSendInHighQuality, + }); + + onAttachmentsUpdate(attachments.map((attachment, i) => { + if (i === editingAttachmentIndex) { + return newAttachment; + } + return attachment; + })); + + setEditingAttachmentIndex(undefined); + }); + const handleResize = useLastCallback(() => { const svg = svgRef.current; if (!svg) { @@ -690,6 +733,7 @@ const AttachmentModal = ({ key={attachment.uniqueId || i} onDelete={handleDelete} onToggleSpoiler={handleToggleSpoiler} + onEdit={!isMobile ? handleEdit : undefined} /> ))}
@@ -789,6 +833,14 @@ const AttachmentModal = ({
+ ); }; @@ -802,7 +854,7 @@ export default memo(withGlobal( attachmentSettings, } = global; - const { shouldSaveAttachmentsCompression } = selectTabState(global); + const { shouldSaveAttachmentsCompression, shouldOpenMessageMediaEditor } = selectTabState(global); const chatFullInfo = selectChatFullInfo(global, chatId); const isChatWithSelf = selectIsChatWithSelf(global, chatId); const { shouldSuggestCustomEmoji } = global.settings.byKey; @@ -822,6 +874,7 @@ export default memo(withGlobal( captionLimit: selectCurrentLimit(global, 'captionLength'), attachmentSettings, shouldSaveAttachmentsCompression, + shouldOpenMessageMediaEditor, }; }, )(AttachmentModal)); diff --git a/src/components/middle/composer/AttachmentModalItem.tsx b/src/components/middle/composer/AttachmentModalItem.tsx index b656dbf8b..6e5ff6798 100644 --- a/src/components/middle/composer/AttachmentModalItem.tsx +++ b/src/components/middle/composer/AttachmentModalItem.tsx @@ -1,4 +1,3 @@ -import type { FC } from '../../../lib/teact/teact'; import { memo, useMemo } from '../../../lib/teact/teact'; import type { ApiAttachment } from '../../../api/types'; @@ -9,6 +8,7 @@ import { formatMediaDuration } from '../../../util/dates/dateFormat'; import { getFileExtension } from '../../common/helpers/documentInfo'; import { REM } from '../../common/helpers/mediaDimensions'; +import useAppLayout from '../../../hooks/useAppLayout'; import useLastCallback from '../../../hooks/useLastCallback'; import File from '../../common/File'; @@ -26,11 +26,12 @@ type OwnProps = { index: number; onDelete?: (index: number) => void; onToggleSpoiler?: (index: number) => void; + onEdit?: (index: number) => void; }; const BLUR_CANVAS_SIZE = 15 * REM; -const AttachmentModalItem: FC = ({ +const AttachmentModalItem = ({ attachment, className, isSingle, @@ -39,13 +40,19 @@ const AttachmentModalItem: FC = ({ index, onDelete, onToggleSpoiler, -}) => { + onEdit, +}: OwnProps) => { + const { isMobile } = useAppLayout(); const displayType = getDisplayType(attachment, shouldDisplayCompressed); const handleSpoilerClick = useLastCallback(() => { onToggleSpoiler?.(index); }); + const handleEditClick = useLastCallback(() => { + onEdit?.(index); + }); + const content = useMemo(() => { switch (displayType) { case 'photo': @@ -73,7 +80,8 @@ const AttachmentModalItem: FC = ({ /> ); - default: + default: { + const canEdit = SUPPORTED_PHOTO_CONTENT_TYPES.has(attachment.mimeType) && !isMobile; return ( <> = ({ previewData={attachment.previewBlobUrl} size={attachment.size} smaller + onClick={canEdit ? handleEditClick : undefined} + actionIcon={canEdit ? 'edit' : undefined} /> {onDelete && ( = ({ )} ); + } } - }, [attachment, displayType, index, onDelete]); + }, [attachment, displayType, index, onDelete, isMobile]); const shouldSkipGrouping = displayType === 'file' || !shouldDisplayGrouped; const shouldDisplaySpoiler = Boolean(displayType !== 'file' && attachment.shouldSendAsSpoiler); @@ -116,6 +127,13 @@ const AttachmentModalItem: FC = ({ /> {shouldRenderOverlay && (
+ {displayType === 'photo' && onEdit && ( + + )} void; + onClear?: NoneToVoidFunction; }; const CLOSE_DURATION = 350; @@ -108,10 +109,12 @@ const ComposerEmbeddedMessage = (props: OwnProps & StateProps) => { exitForwardMode, setShouldPreventComposerAnimation, openSuggestMessageModal, + requestMessageMediaEditor, } = getActions(); const ref = useRef(); const oldLang = useOldLang(); const lang = useLang(); + const { isMobile } = useAppLayout(); const isReplyToTopicStart = message?.content.action?.type === 'topicCreate'; const isShowingSuggestedPost = Boolean(suggestedPostInfo) && !shouldForceShowEditing; @@ -182,6 +185,8 @@ const ComposerEmbeddedMessage = (props: OwnProps & StateProps) => { const isReplyWithQuoteRendering = Boolean(frozenReplyInfo?.quoteText); const isShowingSuggestedPostRendering = Boolean(frozenSuggestedPostInfo) && !frozenShouldForceShowEditing; + const canMediaBeEdited = frozenMessage && canEditMediaInEditor(frozenMessage) && !isMobile; + useEffect(() => { if (shouldPreventComposerAnimation) { setShouldPreventComposerAnimation({ shouldPreventComposerAnimation: false }); @@ -220,6 +225,14 @@ const ComposerEmbeddedMessage = (props: OwnProps & StateProps) => { handleContextMenu(e); }); + const handlePictogramClick = useLastCallback((e: React.MouseEvent): void => { + e.stopPropagation(); + if ((frozenEditingId || frozenReplyInfo?.type === 'message') && canMediaBeEdited) { + requestMessageMediaEditor(); + return; + } + }); + const handleClearClick = useLastCallback((e: React.MouseEvent): void => { e.stopPropagation(); clearEmbedded(); @@ -344,6 +357,8 @@ const ComposerEmbeddedMessage = (props: OwnProps & StateProps) => { title={(frozenEditingId && !isShowingReplyRendering) ? oldLang('EditMessage') : noAuthors ? oldLang('HiddenSendersNameDescription') : undefined} onClick={handleMessageClick} + onPictogramClick={canMediaBeEdited ? handlePictogramClick : undefined} + pictogramActionIcon={canMediaBeEdited ? 'edit' : undefined} senderChat={senderChat} />
+ + {isColorPickerOpen && ( +
+
+
+
+
+ +
+ + +
+
+
+ )} + +
+ + {lang('Size')} + {brushSize} + + +
+ +
{lang('Tool')}
+
+ {DRAW_TOOLS.map((tool) => { + const iconClassName = buildClassName( + 'ListItem-main-icon', + styles.toolIcon, + drawTool === tool.id && styles.toolIconActive, + ); + return ( + onToolChange(tool.id)} + > + + + + + {lang(tool.labelKey)} + + + ); + })} +
+ + ); +} + +export default memo(DrawPanel); diff --git a/src/components/ui/mediaEditor/DrawToolSvgs.tsx b/src/components/ui/mediaEditor/DrawToolSvgs.tsx new file mode 100644 index 000000000..9ea88eabf --- /dev/null +++ b/src/components/ui/mediaEditor/DrawToolSvgs.tsx @@ -0,0 +1,277 @@ +/* eslint-disable @stylistic/max-len */ + +export function PenSvg() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export function ArrowSvg() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export function BrushSvg() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export function NeonSvg() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export function EraserSvg() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/ui/mediaEditor/MediaEditor.module.scss b/src/components/ui/mediaEditor/MediaEditor.module.scss new file mode 100644 index 000000000..e60dfbe2e --- /dev/null +++ b/src/components/ui/mediaEditor/MediaEditor.module.scss @@ -0,0 +1,796 @@ +.root { + position: fixed; + z-index: var(--z-modal); + inset: 0; + transform: scale(0.95); + + display: flex; + + opacity: 0; + background-color: #000; + + transition: opacity 0.2s ease, transform 0.2s ease; + + &:global(.open) { + transform: scale(1); + opacity: 1; + } + + &:global(.closing) { + transform: scale(0.95); + opacity: 0; + } + + :global(body.no-page-transitions) & { + transform: none !important; + transition: none; + } + + @media (max-width: 600px) { + flex-direction: column; + } +} + +.canvasArea { + position: relative; + + display: flex; + flex: 1; + flex-direction: column; + align-items: center; + justify-content: center; + + min-width: 0; + padding: 2rem; + + @media (max-width: 600px) { + padding: 1rem; + } +} + +.canvasContainer { + position: relative; + + overflow: visible; + display: flex; + flex: 1; + align-items: center; + justify-content: center; + + max-width: 100%; + min-height: 0; + max-height: 100%; +} + +.canvas { + display: block; + border-radius: 0.25rem; + + &.drawMode { + cursor: crosshair; + } + + &.transitioningToDraw { + animation: canvasFadeInDelayed 0.3s ease; + } + + &.transitioningToCrop { + animation: canvasZoomOut 0.3s ease; + } + + &.transformAnimating { + animation: canvasTransformReveal 0.3s ease forwards; + } + + &.flipAnimating { + animation: canvasFlipReveal 0.3s ease forwards; + } + + :global(body.no-page-transitions) & { + animation: none !important; + } +} + +// Crop → Draw: canvas stays hidden while snapshot zooms, then fades in +@keyframes canvasFadeInDelayed { + 0% { opacity: 0; } + 60% { opacity: 0; } + 100% { opacity: 1; } +} + +// Draw → Crop: canvas starts zoomed into crop region, then zooms out to full +@keyframes canvasZoomOut { + 0% { + transform: translate(var(--offset-x), var(--offset-y)); + clip-path: + inset( + var(--crop-top) var(--crop-right) var(--crop-bottom) var(--crop-left) round 0.25rem + ); + } + + 100% { + transform: translate(0, 0); + clip-path: inset(0 round 0.25rem); + } +} + +.canvasSnapshot { + pointer-events: none; + + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + border-radius: 0.25rem; + + :global(body.no-page-transitions) & { + display: none; + } + + // Crop → Draw: clip to crop frame, translate to center, then fade out + &.zoomIn { + animation: snapshotZoomIn 0.3s ease forwards; + } + + // Draw → Crop: simple crossfade + &.fadeOut { + animation: snapshotFadeOut 0.3s ease forwards; + } + + &.rotateFade { + animation: snapshotRotateFade 0.3s ease forwards; + } + + &.flipFade { + animation: snapshotFlipFade 0.3s ease forwards; + } +} + +@keyframes snapshotZoomIn { + 0% { + transform: translate(-50%, -50%); + opacity: 1; + clip-path: inset(0 round 0.25rem); + } + + 70% { + transform: + translate( + calc(-50% + var(--offset-x)), + calc(-50% + var(--offset-y)) + ); + opacity: 1; + clip-path: + inset( + var(--crop-top) var(--crop-right) var(--crop-bottom) var(--crop-left) round 0.25rem + ); + } + + 100% { + transform: + translate( + calc(-50% + var(--offset-x)), + calc(-50% + var(--offset-y)) + ); + opacity: 0; + clip-path: + inset( + var(--crop-top) var(--crop-right) var(--crop-bottom) var(--crop-left) round 0.25rem + ); + } +} + +@keyframes snapshotFadeOut { + 0% { transform: translate(-50%, -50%); opacity: 1; } + 25%, 100% { transform: translate(-50%, -50%); opacity: 0; } +} + +@keyframes snapshotRotateFade { + 0% { + transform: translate(-50%, -50%) rotate(0deg) scale(1, 1); + opacity: 1; + } + + 70% { + transform: translate(-50%, -50%) rotate(-90deg) scale(var(--end-sx, 1), var(--end-sy, 1)); + opacity: 1; + } + + 100% { + transform: translate(-50%, -50%) rotate(-90deg) scale(var(--end-sx, 1), var(--end-sy, 1)); + opacity: 0; + } +} + +@keyframes snapshotFlipFade { + 0% { + transform: translate(-50%, -50%) rotateX(0deg) rotateY(0deg); + opacity: 1; + } + + 50% { + transform: translate(-50%, -50%) rotateX(10deg) rotateY(-90deg); + opacity: 1; + } + + 50.01%, 100% { + opacity: 0; + } +} + +@keyframes canvasTransformReveal { + 0%, 65% { opacity: 0; } + 100% { opacity: 1; } +} + +@keyframes canvasFlipReveal { + 0%, 45% { + transform: rotateX(10deg) rotateY(90deg); + } + + 100% { + transform: rotateX(0deg) rotateY(0deg); + } +} + +// Crop overlay - explicitly sized to match canvas, centered in container +.cropWrapper { + pointer-events: none; + + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + animation: fadeIn 0.3s ease; + + &.fadingOut { + animation: fadeOut 0.3s ease forwards; + } + + :global(body.no-page-transitions) & { + animation: none !important; + } +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@keyframes fadeOut { + from { + opacity: 1; + } + + to { + opacity: 0; + } +} + +.cropDarkOverlay { + position: absolute; + inset: 0; + background-color: rgba(0, 0, 0, 0.5); +} + +.cropRegion { + pointer-events: auto; + cursor: move; + position: absolute; + border: 2px solid var(--color-white); +} + +.cropGrid { + position: absolute; + inset: 0; + + &::before, + &::after { + content: ""; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + } + + &::before { + top: 33.33%; + right: 0; + bottom: 33.33%; + left: 0; + + border-top: 1px solid rgba(255, 255, 255, 0.3); + border-bottom: 1px solid rgba(255, 255, 255, 0.3); + + background: transparent; + } + + &::after { + top: 0; + right: 33.33%; + bottom: 0; + left: 33.33%; + + border-right: 1px solid rgba(255, 255, 255, 0.3); + border-left: 1px solid rgba(255, 255, 255, 0.3); + + background: transparent; + } +} + +.cropCorner { + pointer-events: auto; + + position: absolute; + transform: translate(-50%, -50%); + + width: 0.75rem; + height: 0.75rem; + border-radius: 50%; + + background-color: var(--color-white); + + &.topLeft { + cursor: nwse-resize; + } + + &.topRight { + cursor: nesw-resize; + } + + &.bottomLeft { + cursor: nesw-resize; + } + + &.bottomRight { + cursor: nwse-resize; + } +} + +// Right panel (bottom on mobile) +.editPanel { + display: flex; + flex: 0 0 var(--right-column-width); + flex-direction: column; + background-color: var(--color-background); + + @media (max-width: 600px) { + flex-basis: auto; + width: 100%; + max-height: 16rem; + } +} + +.panelHeader { + display: flex; + gap: 0.5rem; + align-items: center; + + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--color-borders); +} + +.headerTitle { + flex: 1; + font-size: 1.125rem; + font-weight: var(--font-weight-medium); + text-align: center; +} + +.headerActions { + display: flex; + gap: 0.25rem; +} + +.panelTabs { + overflow: hidden; + display: flex; + flex: 1; + flex-direction: column-reverse; +} + +.modeTabs { + z-index: 1; + + gap: 1rem; + justify-content: flex-start; + + margin-top: 0.125rem; + padding: 0 1rem; + + background: var(--color-background); + box-shadow: 0 0.125rem 0.125rem var(--color-light-shadow); +} + +.modeTab { + flex: 0.125; + + padding: 0.75rem 0.5rem; + + font-size: 1.25rem; + color: var(--color-text-secondary); + + transition: color 0.15s ease-in-out; + &:hover { + color: var(--color-text); + } + + &:global(.Tab--active) { + color: var(--color-primary); + } +} + +.panelContent { + overflow-x: hidden; + overflow-y: auto; + flex: 1; +} + +// Draw mode +.sectionLabel { + display: flex; + align-items: center; + justify-content: space-between; + + padding: 1rem 1.5rem; + + font-weight: var(--font-weight-semibold); + color: var(--color-text-secondary); +} + +.colorRow { + display: flex; + flex-wrap: wrap; + align-items: center; + padding: 1rem; +} + +.colorSwatch { + cursor: var(--custom-cursor, pointer); + + width: 1.5rem; + height: 1.5rem; + margin: 0.5rem; + padding: 0; + border: none; + border-radius: 50%; + + background-color: var(--swatch-color); + outline: 0.5rem solid transparent !important; + + transition: transform 0.15s ease-in-out, outline-color 0.15s ease-in-out; + + &:hover { + transform: scale(1.1); + } + + &.selected { + outline-color: var(--swatch-outline) !important; + } + + &.customColor { + --swatch-color: transparent; + + background: + conic-gradient( + from 0deg, + #ff0000, + #ff8000, + #ffff00, + #00ff00, + #00ffff, + #0000ff, + #8000ff, + #ff0080, + #ff0000 + ); + + &.selected { + outline-color: #ffffff1a !important; + } + } +} + +.sizeRow { + --selected-color: var(--color-primary); +} + +.sizeSlider { + padding: 0 0.5rem; + + &:global(.RangeSlider) { + --slider-color: var(--selected-color); + } +} + +.sizeValue { + min-width: 1.5rem; + font-size: 0.875rem; + text-align: right; +} + +.canvasControls { + display: flex; + flex-shrink: 0; + gap: 1rem; + align-items: center; + + width: 25rem; + max-width: 100%; + padding: 1rem; + + &.hidden { + pointer-events: none; + opacity: 0; + } + + &.fadingOut { + pointer-events: none; + animation: fadeOut 0.3s ease forwards; + } + + &.fadingIn { + animation: fadeIn 0.3s ease; + } + + :global(body.no-page-transitions) & { + animation: none !important; + } +} + +.aspectRatioList, .toolList { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding: 0 0.5rem; +} + +.toolIcon { + position: relative; + + overflow: hidden; + display: flex; + + width: 7.5rem; + height: 1.25rem; + + &::after { + content: ""; + + position: absolute; + + width: 3.5rem; + height: 1.25rem; + + background: linear-gradient(90deg, var(--color-background) 0%, transparent 100%); + } + + :global(.draw-tool-icon) { + transform: translateX(-1.5rem); + transition: transform 0.15s ease-in-out; + } +} + +.toolLabel { + transform: translateX(-1.5rem); + transition: transform 0.15s ease-in-out; +} + +:global(.ListItem.focus), +:global(.ListItem:hover) { + .toolIcon { + &::after { + background: linear-gradient(90deg, var(--background-color) 0%, transparent 100%); + } + } +} + +:global(.ListItem.focus) { + .toolIcon { + :global(.draw-tool-icon) { + transform: translateX(0); + } + } + .toolLabel { + transform: translateX(0); + } +} + +.aspectRatioRow { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.25rem; +} + +// CSS-based ratio icons +.ratioBox { + + // Base height for all ratio boxes + --ratio-size: 1.25rem; + + margin-inline-start: 0.125rem; + border: 0.125rem solid var(--color-text-secondary); + border-radius: 0.125rem; + + .selected & { + border-color: var(--color-primary); + } + + // Square (1:1) + &.ratio1x1 { + width: var(--ratio-size); + height: var(--ratio-size); + } + + // 3:2 (horizontal) + &.ratio3x2 { + width: var(--ratio-size); + height: calc(var(--ratio-size) * 2 / 3); + } + + // 2:3 (vertical) + &.ratio2x3 { + width: calc(var(--ratio-size) * 2 / 3); + height: var(--ratio-size); + } + + // 4:3 (horizontal) + &.ratio4x3 { + width: var(--ratio-size); + height: calc(var(--ratio-size) * 3 / 4); + } + + // 3:4 (vertical) + &.ratio3x4 { + width: calc(var(--ratio-size) * 3 / 4); + height: var(--ratio-size); + } + + // 5:4 (horizontal) + &.ratio5x4 { + width: var(--ratio-size); + height: calc(var(--ratio-size) * 4 / 5); + } + + // 4:5 (vertical) + &.ratio4x5 { + width: calc(var(--ratio-size) * 4 / 5); + height: var(--ratio-size); + } + + // 16:9 (wide) + &.ratio16x9 { + width: var(--ratio-size); + height: calc(var(--ratio-size) * 9 / 16); + } + + // 9:16 (tall) + &.ratio9x16 { + width: calc(var(--ratio-size) * 9 / 16); + height: var(--ratio-size); + } +} + +// Inline color picker +.colorPickerInline { + display: flex; + flex-direction: column; + padding: 0 1.5rem; +} + +.colorPickerRow { + display: flex; + gap: 0.75rem; + margin-bottom: 1rem; +} + +.saturationBrightness { + cursor: crosshair; + + position: relative; + + flex: 1; + + height: 7.5rem; + border-radius: var(--border-radius-default); + + background: + linear-gradient(to top, #000, transparent), + linear-gradient(to right, #fff, hsl(var(--picker-hue), 100%, 50%)); + + @media (max-width: 600px) { + height: 6rem; + } +} + +.colorInputs { + display: flex; + flex-direction: column; + gap: 0.375rem; + justify-content: center; +} + +.colorInput { + width: 8rem; +} + +.satBrightHandle { + pointer-events: none; + + position: absolute; + top: var(--picker-bright); + left: var(--picker-sat); + transform: translate(-50%, -50%); + + width: 1.25rem; + height: 1.25rem; + border: 0.125rem solid var(--color-white); + border-radius: 50%; + + background-color: var(--picker-color); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +.hueSlider { + cursor: pointer; + + position: relative; + + flex: 1; + + height: 1.5rem; + margin: 0.5rem; + border-radius: 0.75rem; + + background: + linear-gradient( + to right, + #ff0000, + #ff8000, + #ffff00, + #80ff00, + #00ff00, + #00ff80, + #00ffff, + #0080ff, + #0000ff, + #8000ff, + #ff00ff, + #ff0080, + #ff0000 + ); +} + +.hueHandle { + pointer-events: none; + + position: absolute; + top: 50%; + left: calc(var(--picker-hue) / 360 * 100%); + transform: translate(-50%, -50%); + + width: 1.25rem; + height: 1.25rem; + border: 0.125rem solid var(--color-white); + border-radius: 50%; + + background-color: hsl(var(--picker-hue), 100%, 50%); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +// Save button +.saveButton { + position: absolute; + right: 1.5rem; + bottom: 1.5rem; + + @media (max-width: 600px) { + right: 1rem; + bottom: 17rem; + } +} diff --git a/src/components/ui/mediaEditor/MediaEditor.tsx b/src/components/ui/mediaEditor/MediaEditor.tsx new file mode 100644 index 000000000..93f4138c6 --- /dev/null +++ b/src/components/ui/mediaEditor/MediaEditor.tsx @@ -0,0 +1,836 @@ +import { memo, useEffect, useMemo, useRef, useState } from '@teact'; + +import type { DrawAction } from './canvasUtils'; +import type { CropAction, CropState } from './hooks/useCropper'; + +import { selectTheme } from '../../../global/selectors'; +import { selectAnimationLevel } from '../../../global/selectors/sharedState'; +import { IS_WINDOWS } from '../../../util/browser/windowEnvironment'; +import buildClassName from '../../../util/buildClassName'; +import buildStyle from '../../../util/buildStyle'; +import captureEscKeyListener from '../../../util/captureEscKeyListener'; +import getPointerPosition from '../../../util/events/getPointerPosition'; +import { blobToFile, preloadImage } from '../../../util/files'; +import { resolveTransitionName } from '../../../util/resolveTransitionName'; +import { REM } from '../../common/helpers/mediaDimensions'; +import { + applyCanvasTransform, computeRotationZoom, getEffectiveDimensions, renderActionsToCanvas, +} from './canvasUtils'; + +import useSelector from '../../../hooks/data/useSelector'; +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; +import useShowTransition from '../../../hooks/useShowTransition'; +import useCanvasRenderer from './hooks/useCanvasRenderer'; +import useColorPicker, { getPredefinedColors } from './hooks/useColorPicker'; +import useCropper, { DEFAULT_CROP_STATE, getTotalRotation } from './hooks/useCropper'; +import useDisplaySize from './hooks/useDisplaySize'; +import useDrawing from './hooks/useDrawing'; + +import Icon from '../../common/icons/Icon'; +import Button from '../Button'; +import FloatingActionButton from '../FloatingActionButton'; +import Portal from '../Portal'; +import TabList from '../TabList'; +import Transition from '../Transition'; +import CropOverlay from './CropOverlay'; +import CropPanel from './CropPanel'; +import DrawPanel from './DrawPanel'; +import RotationSlider from './RotationSlider'; + +import styles from './MediaEditor.module.scss'; + +type OwnProps = { + isOpen: boolean; + imageUrl?: string; + mimeType?: string; + filename?: string; + onClose: VoidFunction; + onSave: (file: File) => void; +}; + +type EditorMode = 'crop' | 'draw'; + +type EditorAction = DrawAction | CropAction; + +const EDITOR_TABS = [ + { type: 'draw' as const, icon: 'brush' as const }, + { type: 'crop' as const, icon: 'crop' as const }, +]; + +const INITIAL_MODE = 'draw'; +const TABS = EDITOR_TABS.map((tab) => ({ + title: , +})); + +const TRANSITION_DURATION = 300; + +const MediaEditor = ({ + isOpen, + imageUrl, + mimeType, + filename, + onClose, + onSave, +}: OwnProps) => { + const lang = useLang(); + const animationLevel = useSelector(selectAnimationLevel); + const theme = useSelector(selectTheme); + + const predefinedColors = useMemo(() => getPredefinedColors(theme), [theme]); + + const { + ref: rootRef, + shouldRender, + } = useShowTransition({ + isOpen, + withShouldRender: true, + }); + + const transitionRef = useRef(); + const canvasRef = useRef(); + const canvasAreaRef = useRef(); + const originalImageRef = useRef(undefined); + + const [mode, setMode] = useState(INITIAL_MODE); + const [isTransitioning, setIsTransitioning] = useState(false); + const [snapshotSrc, setSnapshotSrc] = useState(); + const [snapshotStyle, setSnapshotStyle] = useState(''); + const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 }); + + const [cropState, setCropState] = useState(DEFAULT_CROP_STATE); + + const effectiveDims = useMemo(() => { + if (imageDimensions.width === 0) return { width: 0, height: 0 }; + return getEffectiveDimensions(imageDimensions.width, imageDimensions.height, cropState.quarterTurns); + }, [imageDimensions.width, imageDimensions.height, cropState.quarterTurns]); + + const [transformAnimType, setTransformAnimType] = useState<'rotate' | 'flip' | undefined>(); + + const [actions, setActions] = useState([]); + const [redoStack, setRedoStack] = useState([]); + + const actionsRef = useRef([]); + const redoStackRef = useRef([]); + actionsRef.current = actions; + redoStackRef.current = redoStack; + + // Display size hook - must be called before useCropper and useCanvasRenderer + const { + displaySize, + getDisplayScale, + resetDisplaySize, + } = useDisplaySize({ + canvasAreaRef, + imageWidth: effectiveDims.width, + imageHeight: effectiveDims.height, + reservedHeight: 6.5 * REM, + }); + + // Color picker hook + const { + hueSliderRef, + satBrightRef, + selectedColor, + setSelectedColor, + isColorPickerOpen, + openColorPicker, + closeColorPicker, + hue, + saturation, + brightness, + pickerColor, + hexInputValue, + rgbInputValue, + handleHueChange, + handleSatBrightChange, + handleHexInput, + handleHexInputBlur, + handleRgbInput, + handleRgbInputBlur, + handleColorSelect, + handleHueSliderMouseDown, + handleSatBrightMouseDown, + } = useColorPicker({ initialColor: predefinedColors[1] }); + + // Get display coordinates for cropper + const getDisplayCoordinates = useLastCallback((e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) => { + const canvas = canvasRef.current; + if (!canvas) return { x: 0, y: 0 }; + + const rect = canvas.getBoundingClientRect(); + const { x: clientX, y: clientY } = getPointerPosition(e as React.MouseEvent); + + return { + x: clientX - rect.left, + y: clientY - rect.top, + }; + }); + + // Handle crop actions + const handleCropAction = useLastCallback((action: CropAction) => { + setActions((prev) => [...prev, action]); + setRedoStack([]); + }); + + // Cropper hook + const { + getCroppedRegion, + initCropState, + handleCropperDragStart, + handleCornerResizeStart, + handleAspectRatioChange, + handleRotationChange, + handleRotationChangeEnd, + handleQuarterRotate, + handleFlip, + } = useCropper({ + imageRef: originalImageRef, + displaySize, + getDisplayScale, + getDisplayCoordinates, + onAction: handleCropAction, + cropState, + setCropState, + }); + + // Memoize drawActions to avoid filtering on every render + const drawActions = useMemo( + () => actions.filter((a): a is DrawAction => a.type === 'draw'), + [actions], + ); + + // Get canvas coordinates for drawing + const getCanvasCoordinates = useLastCallback((e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) => { + const canvas = canvasRef.current; + if (!canvas) return { x: 0, y: 0 }; + + const rect = canvas.getBoundingClientRect(); + const { x: clientX, y: clientY } = getPointerPosition(e as React.MouseEvent); + + const scaleX = canvas.width / rect.width; + const scaleY = canvas.height / rect.height; + + return { + x: (clientX - rect.left) * scaleX, + y: (clientY - rect.top) * scaleY, + }; + }); + + const inverseTransformPoint = useLastCallback(( + x: number, y: number, + effCenterX: number, effCenterY: number, + imgCenterX: number, imgCenterY: number, + zoom: number, + ) => { + const rotation = getTotalRotation(cropState); + const { flipH } = cropState; + + // Translate to effective center + let tx = x - effCenterX; + let ty = y - effCenterY; + + // Inverse rotation + if (rotation !== 0) { + const rad = (-rotation * Math.PI) / 180; + const cos = Math.cos(rad); + const sin = Math.sin(rad); + const newX = tx * cos - ty * sin; + const newY = tx * sin + ty * cos; + tx = newX; + ty = newY; + } + + // Divide by zoom + tx /= zoom; + ty /= zoom; + + // Inverse flip + if (flipH) tx = -tx; + + // Translate back to image center + return { x: tx + imgCenterX, y: ty + imgCenterY }; + }); + + const canvasToImageCoords = useLastCallback((canvasX: number, canvasY: number) => { + const crop = getCroppedRegion(); + const img = originalImageRef.current; + const effectiveX = crop.x + canvasX; + const effectiveY = crop.y + canvasY; + + if (!img || mode !== 'draw') return { x: effectiveX, y: effectiveY }; + + const { width: effW, height: effH } = getEffectiveDimensions( + img.width, img.height, cropState.quarterTurns, + ); + const rotation = getTotalRotation(cropState); + const { flipH } = cropState; + const zoom = computeRotationZoom(effW, effH, cropState.rotation); + + if (rotation === 0 && !flipH && zoom === 1) { + return { x: effectiveX, y: effectiveY }; + } + + return inverseTransformPoint( + effectiveX, effectiveY, + effW / 2, effH / 2, + img.width / 2, img.height / 2, + zoom, + ); + }); + + // Handle draw action complete + const handleDrawActionComplete = useLastCallback((action: DrawAction) => { + setActions((prev) => [...prev, action]); + setRedoStack([]); + }); + + // Drawing hook + const { + drawTool, + setDrawTool, + brushSize, + setBrushSize, + currentDrawAction, + handlePointerDown, + resetDrawing, + } = useDrawing({ + getCanvasCoordinates, + canvasToImageCoords, + selectedColor, + onActionComplete: handleDrawActionComplete, + }); + + // Canvas renderer hook + const { + canvasSize, + renderCanvas, + resetCanvasSize, + } = useCanvasRenderer({ + canvasRef, + imageRef: originalImageRef, + mode, + cropState, + drawActions, + currentDrawAction, + }); + + // Reset state when editor opens + useEffect(() => { + if (isOpen && imageUrl) { + setActions([]); + setRedoStack([]); + resetDrawing(); + setMode(INITIAL_MODE); + setSnapshotSrc(undefined); + setIsTransitioning(false); + setTransformAnimType(undefined); + setSelectedColor(predefinedColors[1]); + setCropState(DEFAULT_CROP_STATE); + resetCanvasSize(); + resetDisplaySize(); + setImageDimensions({ width: 0, height: 0 }); + originalImageRef.current = undefined; + } + // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps + }, [isOpen, imageUrl]); + + // Initialize canvas when image loads + useEffect(() => { + if (!isOpen || !imageUrl) return; + + const initCanvas = async () => { + let image: HTMLImageElement; + try { + image = await preloadImage(imageUrl); + } catch { + return; + } + + originalImageRef.current = image; + setImageDimensions({ width: image.width, height: image.height }); + initCropState(image.width, image.height); + renderCanvas(); + }; + + initCanvas(); + }, [isOpen, imageUrl, renderCanvas, initCropState]); + + // Esc key handler via captureEscKeyListener (participates in shared handler stack) + useEffect(() => { + if (!isOpen) return undefined; + + return captureEscKeyListener(() => { + if (isColorPickerOpen) { + closeColorPicker(); + } else { + onClose(); + } + }); + }, [isOpen, isColorPickerOpen, closeColorPicker, onClose]); + + // Keyboard shortcuts (undo/redo) + useEffect(() => { + if (!isOpen) return undefined; + + const handleKeyDown = (e: KeyboardEvent) => { + const isMeta = e.metaKey || e.ctrlKey; + const key = e.key.toLowerCase(); + + if (isMeta && key === 'z' && !e.shiftKey) { + e.preventDefault(); + handleUndo(); + } else if ((isMeta && key === 'z' && e.shiftKey) || (IS_WINDOWS && isMeta && key === 'y')) { + e.preventDefault(); + handleRedo(); + } + }; + + document.addEventListener('keydown', handleKeyDown); + return () => document.removeEventListener('keydown', handleKeyDown); + }, [isOpen]); + + const handleUndo = useLastCallback(() => { + const actionList = actionsRef.current; + if (actionList.length === 0) return; + + const lastAction = actionList[actionList.length - 1]; + const newActions = actionList.slice(0, -1); + + if (lastAction.type === 'crop') { + const currentState = { ...cropState }; + setCropState(lastAction.previousState); + setRedoStack((prev) => [...prev, { type: 'crop', previousState: currentState }]); + } else { + setRedoStack((prev) => [...prev, lastAction]); + } + setActions(newActions); + }); + + const handleRedo = useLastCallback(() => { + const redo = redoStackRef.current; + if (redo.length === 0) return; + + const actionToRedo = redo[redo.length - 1]; + const newRedoStack = redo.slice(0, -1); + + if (actionToRedo.type === 'crop') { + const currentState = { ...cropState }; + setCropState(actionToRedo.previousState); + setActions((prev) => [...prev, { type: 'crop', previousState: currentState }]); + } else { + setActions((prev) => [...prev, actionToRedo]); + } + setRedoStack(newRedoStack); + }); + + const captureCanvasSnapshot = useLastCallback(( + computeStyle?: (displayWidth: number, displayHeight: number) => string, + ) => { + const canvas = canvasRef.current; + if (!canvas || canvas.width === 0 || canvas.height === 0) return; + + try { + const displayWidth = canvas.offsetWidth; + const displayHeight = canvas.offsetHeight; + const tempCanvas = document.createElement('canvas'); + tempCanvas.width = displayWidth; + tempCanvas.height = displayHeight; + const ctx = tempCanvas.getContext('2d'); + if (ctx) { + ctx.drawImage(canvas, 0, 0, displayWidth, displayHeight); + setSnapshotSrc(tempCanvas.toDataURL()); + setSnapshotStyle( + computeStyle + ? computeStyle(displayWidth, displayHeight) + : `width: ${displayWidth}px; height: ${displayHeight}px`, + ); + } + } catch { + // Canvas might be tainted + } + }); + + const handleQuarterRotateAnimated = useLastCallback(() => { + if (animationLevel > 0) { + captureCanvasSnapshot((oldW, oldH) => { + // Compute scale factors so the rotated snapshot matches the new canvas size + const canvasArea = canvasAreaRef.current; + if (!canvasArea) return `width: ${oldW}px; height: ${oldH}px`; + + const newEffDims = getEffectiveDimensions( + imageDimensions.width, imageDimensions.height, + (cropState.quarterTurns + 1) % 4, + ); + const areaRect = canvasArea.getBoundingClientRect(); + const areaStyle = getComputedStyle(canvasArea); + const padX = parseFloat(areaStyle.paddingLeft) + parseFloat(areaStyle.paddingRight); + const padY = parseFloat(areaStyle.paddingTop) + parseFloat(areaStyle.paddingBottom); + const scaleToFit = Math.min( + (areaRect.width - padX) / newEffDims.width, + (areaRect.height - padY - 6.5 * REM) / newEffDims.height, + 1, + ); + const newW = newEffDims.width * scaleToFit; + const newH = newEffDims.height * scaleToFit; + + // After CSS rotate(-90deg) scale(sx, sy), visual bounds = (oldH*sy, oldW*sx) + const sx = newH / oldW; + const sy = newW / oldH; + return `width: ${oldW}px; height: ${oldH}px; --end-sx: ${sx}; --end-sy: ${sy}`; + }); + setTransformAnimType('rotate'); + } + handleQuarterRotate(); + if (animationLevel > 0) { + setTimeout(() => { + setTransformAnimType(undefined); + setSnapshotSrc(undefined); + }, TRANSITION_DURATION); + } + }); + + const handleFlipAnimated = useLastCallback(() => { + if (animationLevel > 0) { + captureCanvasSnapshot(); + setTransformAnimType('flip'); + } + handleFlip(); + if (animationLevel > 0) { + setTimeout(() => { + setTransformAnimType(undefined); + setSnapshotSrc(undefined); + }, TRANSITION_DURATION); + } + }); + + const handleSave = useLastCallback(() => { + const img = originalImageRef.current; + if (!img) return; + + const crop = getCroppedRegion(); + if (crop.width <= 0 || crop.height <= 0) return; + + const rotation = getTotalRotation(cropState); + const { flipH } = cropState; + const { width: effW, height: effH } = getEffectiveDimensions( + img.width, img.height, cropState.quarterTurns, + ); + const zoom = computeRotationZoom(effW, effH, cropState.rotation); + const hasTransforms = rotation !== 0 || flipH || cropState.quarterTurns !== 0 || zoom !== 1; + + // Stage 1: Render full image with transforms at effective dims + const fullCanvas = document.createElement('canvas'); + fullCanvas.width = effW; + fullCanvas.height = effH; + const fullCtx = fullCanvas.getContext('2d'); + if (!fullCtx) return; + + if (hasTransforms) { + fullCtx.save(); + applyCanvasTransform(fullCtx, img, rotation, flipH, cropState.quarterTurns, zoom); + } + + fullCtx.drawImage(img, 0, 0); + renderActionsToCanvas(fullCtx, drawActions, 0, 0, undefined, img.width, img.height); + + if (hasTransforms) { + fullCtx.restore(); + } + + // Stage 2: Crop from effective space + const finalCanvas = document.createElement('canvas'); + finalCanvas.width = Math.round(crop.width); + finalCanvas.height = Math.round(crop.height); + const ctx = finalCanvas.getContext('2d'); + if (!ctx) return; + + ctx.drawImage(fullCanvas, crop.x, crop.y, crop.width, crop.height, 0, 0, crop.width, crop.height); + + const mimeTypeToUse = mimeType || 'image/jpeg'; + finalCanvas.toBlob((blob) => { + if (blob) { + const resultFilename = filename || `image.${getExtensionFromMimeType(mimeTypeToUse)}`; + const file = blobToFile(blob, resultFilename); + onSave(file); + onClose(); + } + }, mimeTypeToUse); + }); + + const activeTabIndex = EDITOR_TABS.findIndex((tab) => tab.type === mode); + + const handleTabSwitch = useLastCallback((index: number) => { + const tab = EDITOR_TABS[index]; + if (tab && tab.type !== mode) { + if (animationLevel > 0) { + if (tab.type === 'draw') { + // Crop → Draw: compute crop frame for zoom animation + captureCanvasSnapshot((displayWidth, displayHeight) => { + const scale = getDisplayScale(); + const fW = cropState.cropperWidth * scale; + const fH = cropState.cropperHeight * scale; + const fX = cropState.cropperX * scale; + const fY = cropState.cropperY * scale; + + return buildStyle( + `width: ${displayWidth}px`, + `height: ${displayHeight}px`, + `--crop-top: ${fY}px`, + `--crop-right: ${displayWidth - (fX + fW)}px`, + `--crop-bottom: ${displayHeight - (fY + fH)}px`, + `--crop-left: ${fX}px`, + `--offset-x: ${(displayWidth / 2) - (fX + fW / 2)}px`, + `--offset-y: ${(displayHeight / 2) - (fY + fH / 2)}px`, + ); + }); + } else { + captureCanvasSnapshot(); + } + setIsTransitioning(true); + setTimeout(() => { + setIsTransitioning(false); + setSnapshotSrc(undefined); + }, TRANSITION_DURATION); + } + setMode(tab.type); + } + }); + + const canUndo = actions.length > 0; + const canRedo = redoStack.length > 0; + + if (!shouldRender) return undefined; + + const renderPanelContent = () => { + switch (mode) { + case 'crop': + return ( + + ); + case 'draw': + return ( + + ); + default: + return undefined; + } + }; + + const isTransitioningToDraw = isTransitioning && mode === 'draw'; + const isTransitioningToCrop = isTransitioning && mode === 'crop'; + const shouldShowCropOverlay = mode === 'crop' || isTransitioningToDraw; + const displayScale = getDisplayScale(); + + const canvasStyle = useMemo(() => { + if (displaySize.width === 0) return ''; + + if (mode === 'crop') { + const baseStyle = buildStyle( + `width: ${displaySize.width}px`, + `height: ${displaySize.height}px`, + ); + + if (isTransitioning) { + // Draw → Crop: pass crop frame vars for zoom-out animation + const fW = cropState.cropperWidth * displayScale; + const fH = cropState.cropperHeight * displayScale; + const fX = cropState.cropperX * displayScale; + const fY = cropState.cropperY * displayScale; + + return buildStyle( + baseStyle, + `--crop-top: ${fY}px`, + `--crop-right: ${displaySize.width - (fX + fW)}px`, + `--crop-bottom: ${displaySize.height - (fY + fH)}px`, + `--crop-left: ${fX}px`, + `--offset-x: ${(displaySize.width / 2) - (fX + fW / 2)}px`, + `--offset-y: ${(displaySize.height / 2) - (fY + fH / 2)}px`, + ); + } + + return baseStyle; + } + + const frameWidth = cropState.cropperWidth * displayScale; + const frameHeight = cropState.cropperHeight * displayScale; + + return buildStyle( + `width: ${frameWidth}px`, + `height: ${frameHeight}px`, + ); + }, [displaySize, cropState, displayScale, mode, isTransitioning]); + + return ( + +
+
+
+ + {snapshotSrc && ( + + )} + {shouldShowCropOverlay && !transformAnimType && displaySize.width > 0 && ( + + )} +
+ +
+
+
+ +
+
+ +
{lang('EditMedia')}
+
+
+
+ +
+ + {renderPanelContent()} + + +
+
+ + 0} + iconName="check" + className={styles.saveButton} + onClick={handleSave} + ariaLabel={lang('Save')} + /> +
+
+ ); +}; + +function getExtensionFromMimeType(mimeType: string): string { + return mimeType.split('/')[1]; +} + +export default memo(MediaEditor); diff --git a/src/components/ui/mediaEditor/RotationSlider.module.scss b/src/components/ui/mediaEditor/RotationSlider.module.scss new file mode 100644 index 000000000..ddfda16c6 --- /dev/null +++ b/src/components/ui/mediaEditor/RotationSlider.module.scss @@ -0,0 +1,104 @@ +.root { + position: relative; + width: 100%; +} + +.slider { + touch-action: none; + cursor: grab; + + position: relative; + + overflow: hidden; + + height: 3rem; + + // Dark overlay that dims dots except at center, creating a spotlight effect + &::after { + pointer-events: none; + content: ''; + + position: absolute; + z-index: 2; + top: 2rem; + right: 0; + left: 0; + + height: 0.25rem; + + background: radial-gradient(0.75rem 50% at center, transparent, rgba(0, 0, 0, 0.65)); + } + + &:active { + cursor: grabbing; + } +} + +.track { + will-change: transform; + + position: absolute; + top: 0; + left: 50%; + + height: 100%; +} + +.labelsRow { + position: relative; + height: 1.25rem; +} + +.label, +.labelActive { + position: absolute; + top: 0; + transform: translateX(-50%); + + font-size: 0.75rem; + color: rgba(255, 255, 255, 0.4); + white-space: nowrap; + + &::after { + content: '°'; + position: absolute; + top: -0.25rem; + right: -0.375rem; + } +} + +.labelActive { + font-size: 0.875rem; + font-weight: var(--font-weight-semibold); + color: #fff; +} + +.centerIndicator { + pointer-events: none; + + position: absolute; + z-index: 1; + top: 1.375rem; + left: 50%; + transform: translateX(-50%); + + width: 0; + height: 0; + border-right: 0.25rem solid transparent; + border-bottom: 0.3125rem solid #fff; + border-left: 0.25rem solid transparent; +} + +.dotsRow { + position: absolute; + top: 2rem; + // 91 dots spanning -90° to 90° every 2°, at 5px/degree = 10px spacing + // Offset by half a tile so first dot lands at -90° position + left: -28.4375rem; + + width: 56.875rem; + height: 0.25rem; + + background-image: radial-gradient(circle, rgba(255, 255, 255, 0.8) 0.0625rem, transparent 0.0625rem); + background-size: 0.625rem 0.25rem; +} diff --git a/src/components/ui/mediaEditor/RotationSlider.tsx b/src/components/ui/mediaEditor/RotationSlider.tsx new file mode 100644 index 000000000..23aac1e5e --- /dev/null +++ b/src/components/ui/mediaEditor/RotationSlider.tsx @@ -0,0 +1,84 @@ +import { memo } from '@teact'; + +import getPointerPosition from '../../../util/events/getPointerPosition'; +import { clamp } from '../../../util/math'; + +import useLastCallback from '../../../hooks/useLastCallback'; + +import styles from './RotationSlider.module.scss'; + +type OwnProps = { + value: number; + onChange: (value: number) => void; + onChangeEnd?: NoneToVoidFunction; +}; + +const MIN_ROTATION = -90; +const MAX_ROTATION = 90; +const LABEL_INTERVAL = 15; +const PIXELS_PER_DEGREE = 5; + +function RotationSlider({ value, onChange, onChangeEnd }: OwnProps) { + const handlePointerDown = useLastCallback((e: React.MouseEvent | React.TouchEvent) => { + e.preventDefault(); + const { x: startX } = getPointerPosition(e); + const startValue = value; + + const handleMove = (ev: MouseEvent | TouchEvent) => { + ev.preventDefault(); + const { x: clientX } = getPointerPosition(ev); + const deltaX = clientX - startX; + const newValue = clamp(Math.round(startValue - deltaX / PIXELS_PER_DEGREE), MIN_ROTATION, MAX_ROTATION); + onChange(newValue); + }; + + const handleUp = () => { + document.removeEventListener('mousemove', handleMove); + document.removeEventListener('touchmove', handleMove); + document.removeEventListener('mouseup', handleUp); + document.removeEventListener('touchend', handleUp); + onChangeEnd?.(); + }; + + document.addEventListener('mousemove', handleMove); + document.addEventListener('touchmove', handleMove, { passive: false }); + document.addEventListener('mouseup', handleUp); + document.addEventListener('touchend', handleUp); + }); + + const nearestLabel = Math.round(value / LABEL_INTERVAL) * LABEL_INTERVAL; + const trackOffset = -value * PIXELS_PER_DEGREE; + + const labels = []; + for (let deg = MIN_ROTATION; deg <= MAX_ROTATION; deg += LABEL_INTERVAL) { + labels.push( + + {deg} + , + ); + } + + return ( +
+
+
+
+ {labels} +
+
+
+
+
+
+ ); +} + +export default memo(RotationSlider); diff --git a/src/components/ui/mediaEditor/canvasUtils.ts b/src/components/ui/mediaEditor/canvasUtils.ts new file mode 100644 index 000000000..70450263d --- /dev/null +++ b/src/components/ui/mediaEditor/canvasUtils.ts @@ -0,0 +1,226 @@ +export type DrawTool = 'pen' | 'arrow' | 'brush' | 'neon' | 'eraser'; + +export const ARROW_ANIMATION_DURATION = 200; +const offscreen = document.createElement('canvas'); + +export function getEffectiveDimensions(imgWidth: number, imgHeight: number, quarterTurns: number) { + const isSideways = quarterTurns % 2 === 1; + return { + width: isSideways ? imgHeight : imgWidth, + height: isSideways ? imgWidth : imgHeight, + }; +} + +export function computeRotationZoom(effectiveW: number, effectiveH: number, fineRotation: number) { + if (fineRotation === 0 || effectiveW <= 0 || effectiveH <= 0) return 1; + const rad = Math.abs(fineRotation * Math.PI / 180); + const cos = Math.cos(rad); + const sin = Math.sin(rad); + return Math.max( + cos + (effectiveH / effectiveW) * sin, + (effectiveW / effectiveH) * sin + cos, + ); +} + +export interface DrawAction { + type: 'draw'; + tool: DrawTool; + points: Array<{ x: number; y: number }>; + color: string; + brushSize: number; + completedAt?: number; + isShiftPressed?: boolean; +} + +export function renderDrawAction( + ctx: CanvasRenderingContext2D, + action: DrawAction, + offsetX = 0, + offsetY = 0, + isComplete = true, +) { + if (action.points.length < 2) return; + + ctx.save(); + + if (action.tool === 'eraser') { + ctx.globalCompositeOperation = 'destination-out'; + ctx.strokeStyle = 'rgba(0,0,0,1)'; + ctx.lineWidth = action.brushSize * 2; + } else if (action.tool === 'neon') { + ctx.shadowColor = action.color; + ctx.shadowBlur = action.brushSize * 2; + ctx.strokeStyle = action.color; + ctx.lineWidth = action.brushSize * 0.5; + } else if (action.tool === 'brush') { + ctx.globalAlpha = 0.4; + ctx.strokeStyle = action.color; + ctx.lineWidth = action.brushSize * 2; + } else { + ctx.strokeStyle = action.color; + ctx.lineWidth = action.brushSize; + } + + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + + if (action.tool === 'arrow') { + renderArrow(ctx, action, offsetX, offsetY, isComplete); + } else { + renderPath(ctx, action, offsetX, offsetY); + } + + ctx.restore(); +} + +function renderArrow( + ctx: CanvasRenderingContext2D, + action: DrawAction, + offsetX: number, + offsetY: number, + isComplete: boolean, +) { + if (action.points.length < 2) return; + + const firstPoint = action.points[0]; + const lastPoint = action.points[action.points.length - 1]; + + // Draw the path + ctx.beginPath(); + ctx.moveTo(firstPoint.x + offsetX, firstPoint.y + offsetY); + + for (let i = 1; i < action.points.length; i++) { + const point = action.points[i]; + ctx.lineTo(point.x + offsetX, point.y + offsetY); + } + ctx.stroke(); + + // Only draw arrowhead when drawing is complete + if (!isComplete) return; + + // Calculate angle from a point further back for stable direction that follows the path + // Use a point 10 steps back, or the first point if path is shorter + const lookbackIndex = Math.max(0, action.points.length - 10); + const referencePoint = action.points[lookbackIndex]; + + const angle = Math.atan2( + lastPoint.y - referencePoint.y, + lastPoint.x - referencePoint.x, + ); + + // Animate arrowhead appearance + const elapsed = action.completedAt ? Date.now() - action.completedAt : ARROW_ANIMATION_DURATION; + const progress = Math.min(elapsed / ARROW_ANIMATION_DURATION, 1); + // Ease out cubic for smooth animation + const easedProgress = 1 - ((1 - progress) ** 3); + + const headLength = action.brushSize * 3 * easedProgress; + + ctx.beginPath(); + ctx.moveTo(lastPoint.x + offsetX, lastPoint.y + offsetY); + ctx.lineTo( + lastPoint.x + offsetX - headLength * Math.cos(angle - Math.PI / 6), + lastPoint.y + offsetY - headLength * Math.sin(angle - Math.PI / 6), + ); + ctx.moveTo(lastPoint.x + offsetX, lastPoint.y + offsetY); + ctx.lineTo( + lastPoint.x + offsetX - headLength * Math.cos(angle + Math.PI / 6), + lastPoint.y + offsetY - headLength * Math.sin(angle + Math.PI / 6), + ); + ctx.stroke(); +} + +function renderPath( + ctx: CanvasRenderingContext2D, + action: DrawAction, + offsetX: number, + offsetY: number, +) { + ctx.beginPath(); + const firstPoint = action.points[0]; + ctx.moveTo(firstPoint.x + offsetX, firstPoint.y + offsetY); + + for (let i = 1; i < action.points.length; i++) { + const point = action.points[i]; + ctx.lineTo(point.x + offsetX, point.y + offsetY); + } + ctx.stroke(); +} + +export function applyCanvasTransform( + ctx: CanvasRenderingContext2D, + image: HTMLImageElement, + rotation: number, + flipH: boolean, + quarterTurns = 0, + scale = 1, +) { + const { width: effW, height: effH } = getEffectiveDimensions(image.width, image.height, quarterTurns); + ctx.translate(effW / 2, effH / 2); + ctx.rotate((rotation * Math.PI) / 180); + ctx.scale(scale * (flipH ? -1 : 1), scale); + ctx.translate(-image.width / 2, -image.height / 2); +} + +export function renderImageToCanvas( + ctx: CanvasRenderingContext2D, + img: HTMLImageElement, + crop: { x: number; y: number; width: number; height: number }, + targetWidth: number, + targetHeight: number, + isCropMode: boolean, + rotation = 0, + flipH = false, + quarterTurns = 0, + scale = 1, +) { + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + + ctx.save(); + + if (rotation !== 0 || flipH || quarterTurns !== 0 || scale !== 1) { + applyCanvasTransform(ctx, img, rotation, flipH, quarterTurns, scale); + } + + if (isCropMode) { + ctx.drawImage(img, 0, 0); + } else { + ctx.drawImage( + img, + crop.x, crop.y, crop.width, crop.height, + 0, 0, targetWidth, targetHeight, + ); + } + + ctx.restore(); +} + +export function renderActionsToCanvas( + ctx: CanvasRenderingContext2D, + actions: DrawAction[], + offsetX = 0, + offsetY = 0, + currentAction?: DrawAction, + offscreenWidth?: number, + offscreenHeight?: number, +) { + const hasCurrentAction = currentAction && !actions.includes(currentAction); + if (actions.length === 0 && !hasCurrentAction) return; + + const width = offscreenWidth || ctx.canvas.width; + const height = offscreenHeight || ctx.canvas.height; + offscreen.width = width; + offscreen.height = height; + const offCtx = offscreen.getContext('2d')!; + offCtx.clearRect(0, 0, width, height); + + actions.forEach((action) => { + renderDrawAction(offCtx, action, offsetX, offsetY, true); + }); + + if (hasCurrentAction) { + renderDrawAction(offCtx, currentAction, offsetX, offsetY, false); + } + + ctx.drawImage(offscreen, 0, 0); +} diff --git a/src/components/ui/mediaEditor/hooks/useCanvasRenderer.ts b/src/components/ui/mediaEditor/hooks/useCanvasRenderer.ts new file mode 100644 index 000000000..7919d4d0a --- /dev/null +++ b/src/components/ui/mediaEditor/hooks/useCanvasRenderer.ts @@ -0,0 +1,155 @@ +import { useEffect, useMemo, useRef, useState } from '@teact'; + +import type { DrawAction } from '../canvasUtils'; +import type { CropState } from './useCropper'; + +import { fastRaf, throttleWith } from '../../../../util/schedulers'; +import { + applyCanvasTransform, ARROW_ANIMATION_DURATION, computeRotationZoom, + getEffectiveDimensions, renderActionsToCanvas, renderImageToCanvas, +} from '../canvasUtils'; +import { getTotalRotation } from './useCropper'; + +import useLastCallback from '../../../../hooks/useLastCallback'; + +interface UseCanvasRendererOptions { + canvasRef: React.RefObject; + imageRef: React.RefObject; + mode: 'crop' | 'draw'; + cropState: CropState; + drawActions: DrawAction[]; + currentDrawAction?: DrawAction; +} + +export default function useCanvasRenderer({ + canvasRef, + imageRef, + mode, + cropState, + drawActions, + currentDrawAction, +}: UseCanvasRendererOptions) { + const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 }); + + const renderCanvas = useLastCallback(() => { + const canvas = canvasRef.current; + const img = imageRef.current; + if (!canvas || !img) return; + + const crop = { + x: cropState.cropperX, y: cropState.cropperY, + width: cropState.cropperWidth, height: cropState.cropperHeight, + }; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const rotation = getTotalRotation(cropState); + const { flipH } = cropState; + + if (mode === 'crop') { + const { width: effW, height: effH } = getEffectiveDimensions( + img.width, img.height, cropState.quarterTurns, + ); + const zoom = computeRotationZoom(effW, effH, cropState.rotation); + + if (canvasSize.width !== effW || canvasSize.height !== effH) { + setCanvasSize({ width: effW, height: effH }); + return; + } + + renderImageToCanvas(ctx, img, crop, effW, effH, true, rotation, flipH, cropState.quarterTurns, zoom); + + const hasTransforms = rotation !== 0 || flipH || cropState.quarterTurns !== 0 || zoom !== 1; + if (hasTransforms) { + ctx.save(); + applyCanvasTransform(ctx, img, rotation, flipH, cropState.quarterTurns, zoom); + renderActionsToCanvas(ctx, drawActions, 0, 0, undefined, img.width, img.height); + ctx.restore(); + } else { + renderActionsToCanvas(ctx, drawActions); + } + } else { + if (crop.width <= 0 || crop.height <= 0) return; + + const targetWidth = Math.round(crop.width); + const targetHeight = Math.round(crop.height); + + if (canvasSize.width !== targetWidth || canvasSize.height !== targetHeight) { + setCanvasSize({ width: targetWidth, height: targetHeight }); + return; + } + + const { width: effW, height: effH } = getEffectiveDimensions( + img.width, img.height, cropState.quarterTurns, + ); + const zoom = computeRotationZoom(effW, effH, cropState.rotation); + const hasTransforms = rotation !== 0 || flipH || cropState.quarterTurns !== 0 || zoom !== 1; + + // Create temp canvas at effective dimensions + const tempCanvas = document.createElement('canvas'); + tempCanvas.width = effW; + tempCanvas.height = effH; + const tempCtx = tempCanvas.getContext('2d')!; + + if (hasTransforms) { + tempCtx.save(); + applyCanvasTransform(tempCtx, img, rotation, flipH, cropState.quarterTurns, zoom); + } + + // Draw image and actions (in image coords, transformed to effective space) + tempCtx.drawImage(img, 0, 0); + renderActionsToCanvas(tempCtx, drawActions, 0, 0, currentDrawAction, img.width, img.height); + + if (hasTransforms) { + tempCtx.restore(); + } + + // Crop from effective space + ctx.drawImage(tempCanvas, crop.x, crop.y, crop.width, crop.height, 0, 0, targetWidth, targetHeight); + } + }); + + // Throttle re-renders to one per animation frame + const scheduleRender = useMemo(() => throttleWith(fastRaf, renderCanvas), [renderCanvas]); + + // Re-render canvas when dependencies change + useEffect(() => { + scheduleRender(); + }, [drawActions, currentDrawAction, canvasSize, mode, cropState, scheduleRender]); + + // Animation loop for arrow spreading effect + const animationFrameRef = useRef(); + useEffect(() => { + const hasAnimatingArrow = () => drawActions.some((action) => { + return action.tool === 'arrow' && action.completedAt + && (Date.now() - action.completedAt) < ARROW_ANIMATION_DURATION; + }); + + if (!hasAnimatingArrow()) return undefined; + + const animate = () => { + renderCanvas(); + if (hasAnimatingArrow()) { + animationFrameRef.current = requestAnimationFrame(animate); + } + }; + + animationFrameRef.current = requestAnimationFrame(animate); + + return () => { + if (animationFrameRef.current) { + cancelAnimationFrame(animationFrameRef.current); + } + }; + }, [drawActions, renderCanvas]); + + const resetCanvasSize = useLastCallback(() => { + setCanvasSize({ width: 0, height: 0 }); + }); + + return { + canvasSize, + renderCanvas, + resetCanvasSize, + }; +} diff --git a/src/components/ui/mediaEditor/hooks/useColorPicker.ts b/src/components/ui/mediaEditor/hooks/useColorPicker.ts new file mode 100644 index 000000000..66b948039 --- /dev/null +++ b/src/components/ui/mediaEditor/hooks/useColorPicker.ts @@ -0,0 +1,199 @@ +import { useEffect, useRef, useState } from '@teact'; + +import { hex2rgb, hsv2rgb, rgb2hex, rgb2hsv } from '../../../../util/colors'; +import getPointerPosition from '../../../../util/events/getPointerPosition'; +import { clamp } from '../../../../util/math'; + +import useFlag from '../../../../hooks/useFlag'; +import useLastCallback from '../../../../hooks/useLastCallback'; + +const PREDEFINED_COLORS_BASE = [ + '#FE4438', '#FF8901', '#FFD60A', '#33C759', + '#62E5E0', '#0A84FF', '#5856D6', '#BD5CF3', +]; + +export function getPredefinedColors(theme: 'light' | 'dark') { + return theme === 'light' + ? ['#000000', ...PREDEFINED_COLORS_BASE] + : ['#FFFFFF', ...PREDEFINED_COLORS_BASE]; +} + +interface PickerState { + hue: number; + saturation: number; + brightness: number; + hexInputValue: string; + rgbInputValue: string; +} + +function buildPickerState(h: number, s: number, v: number): PickerState { + const rgb = hsv2rgb([h, s, v]); + const hex = rgb2hex(rgb); + return { + hue: h, + saturation: s, + brightness: v, + hexInputValue: hex.toUpperCase(), + rgbInputValue: `${rgb[0]}, ${rgb[1]}, ${rgb[2]}`, + }; +} + +const DEFAULT_PICKER_STATE: PickerState = { + hue: 0, + saturation: 1, + brightness: 1, + hexInputValue: '', + rgbInputValue: '', +}; + +interface UseColorPickerOptions { + initialColor: string; +} + +export default function useColorPicker({ initialColor }: UseColorPickerOptions) { + const hueSliderRef = useRef(); + const satBrightRef = useRef(); + + const [selectedColor, setSelectedColor] = useState(initialColor); + const [isColorPickerOpen, openColorPicker, closeColorPicker] = useFlag(false); + const [pickerState, setPickerState] = useState(DEFAULT_PICKER_STATE); + + const pickerColor = rgb2hex(hsv2rgb([pickerState.hue, pickerState.saturation, pickerState.brightness])); + + useEffect(() => { + if (!isColorPickerOpen) return; + const rgb = hex2rgb(selectedColor.replace('#', '')); + const [h, s, v] = rgb2hsv(rgb); + setPickerState(buildPickerState(h, s, v)); + // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps + }, [isColorPickerOpen]); + + const updateFromHsv = useLastCallback((h: number, s: number, v: number) => { + const state = buildPickerState(h, s, v); + setPickerState(state); + setSelectedColor(rgb2hex(hsv2rgb([h, s, v]))); + }); + + const setupColorDrag = useLastCallback(( + handler: (e: MouseEvent | TouchEvent) => void, + ) => { + const handleMove = (ev: MouseEvent) => handler(ev); + const handleUp = () => { + document.removeEventListener('mousemove', handleMove); + document.removeEventListener('mouseup', handleUp); + }; + document.addEventListener('mousemove', handleMove); + document.addEventListener('mouseup', handleUp); + }); + + const handleHueChange = useLastCallback((e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) => { + const el = hueSliderRef.current; + if (!el) return; + const rect = el.getBoundingClientRect(); + const { x: clientX } = getPointerPosition(e as React.MouseEvent); + const x = clamp(clientX - rect.left, 0, rect.width); + updateFromHsv(x / rect.width, pickerState.saturation, pickerState.brightness); + }); + + const handleSatBrightChange = useLastCallback((e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) => { + const el = satBrightRef.current; + if (!el) return; + const rect = el.getBoundingClientRect(); + const { x: clientX, y: clientY } = getPointerPosition(e as React.MouseEvent); + const x = clamp(clientX - rect.left, 0, rect.width); + const y = clamp(clientY - rect.top, 0, rect.height); + updateFromHsv(pickerState.hue, x / rect.width, 1 - y / rect.height); + }); + + const handleHexInput = useLastCallback((e: React.ChangeEvent) => { + const cleanHex = e.target.value.toUpperCase().replace(/[^0-9A-F]/g, '').slice(0, 6); + // Force the DOM input to show the cleaned value immediately + e.target.value = `#${cleanHex}`; + + // Expand 3-char shortcode (#EEE -> #EEEEEE) or use 6-char hex + const fullHex = cleanHex.length === 3 + ? cleanHex.split('').map((c) => c + c).join('') + : cleanHex; + + if (fullHex.length === 6) { + const [h, s, v] = rgb2hsv(hex2rgb(fullHex)); + const state = buildPickerState(h, s, v); + // Preserve the raw typed hex while updating HSV + rgb + setPickerState({ ...state, hexInputValue: `#${cleanHex}` }); + setSelectedColor(rgb2hex(hsv2rgb([h, s, v]))); + } else { + setPickerState((prev) => ({ ...prev, hexInputValue: `#${cleanHex}` })); + } + }); + + const handleRgbInput = useLastCallback((e: React.ChangeEvent) => { + const value = e.target.value; + + const parts = value.split(',').map((p) => p.trim()); + if (parts.length === 3) { + const r = parseInt(parts[0], 10); + const g = parseInt(parts[1], 10); + const b = parseInt(parts[2], 10); + + if (![r, g, b].some((v) => Number.isNaN(v) || v < 0 || v > 255)) { + const [h, s, v] = rgb2hsv([r, g, b]); + const state = buildPickerState(h, s, v); + // Preserve the raw typed rgb while updating HSV + hex + setPickerState({ ...state, rgbInputValue: value }); + setSelectedColor(rgb2hex(hsv2rgb([h, s, v]))); + return; + } + } + + setPickerState((prev) => ({ ...prev, rgbInputValue: value })); + }); + + const handleHexInputBlur = useLastCallback(() => { + setPickerState((prev) => ({ ...prev, hexInputValue: pickerColor.toUpperCase() })); + }); + + const handleRgbInputBlur = useLastCallback(() => { + const rgb = hsv2rgb([pickerState.hue, pickerState.saturation, pickerState.brightness]); + setPickerState((prev) => ({ ...prev, rgbInputValue: `${rgb[0]}, ${rgb[1]}, ${rgb[2]}` })); + }); + + const handleColorSelect = useLastCallback((color: string) => { + setSelectedColor(color); + closeColorPicker(); + }); + + const handleHueSliderMouseDown = useLastCallback((e: React.MouseEvent) => { + handleHueChange(e); + setupColorDrag(handleHueChange); + }); + + const handleSatBrightMouseDown = useLastCallback((e: React.MouseEvent) => { + handleSatBrightChange(e); + setupColorDrag(handleSatBrightChange); + }); + + return { + hueSliderRef, + satBrightRef, + selectedColor, + setSelectedColor, + isColorPickerOpen, + openColorPicker, + closeColorPicker, + hue: pickerState.hue, + saturation: pickerState.saturation, + brightness: pickerState.brightness, + pickerColor, + hexInputValue: pickerState.hexInputValue, + rgbInputValue: pickerState.rgbInputValue, + handleHueChange, + handleSatBrightChange, + handleHexInput, + handleHexInputBlur, + handleRgbInput, + handleRgbInputBlur, + handleColorSelect, + handleHueSliderMouseDown, + handleSatBrightMouseDown, + }; +} diff --git a/src/components/ui/mediaEditor/hooks/useCropper.ts b/src/components/ui/mediaEditor/hooks/useCropper.ts new file mode 100644 index 000000000..c78c6e48c --- /dev/null +++ b/src/components/ui/mediaEditor/hooks/useCropper.ts @@ -0,0 +1,474 @@ +import { useRef } from '@teact'; + +import { clamp } from '../../../../util/math'; +import { getEffectiveDimensions } from '../canvasUtils'; + +import useLastCallback from '../../../../hooks/useLastCallback'; + +export interface CropState { + cropperX: number; + cropperY: number; + cropperWidth: number; + cropperHeight: number; + aspectRatio: AspectRatio; + rotation: number; + quarterTurns: number; + flipH: boolean; +} + +export function getTotalRotation(state: CropState): number { + return state.rotation - state.quarterTurns * 90; +} + +export type AspectRatio = + 'free' | 'original' | 'square' | '3:2' | '2:3' | '4:3' | '3:4' | '5:4' | '4:5' | '16:9' | '9:16'; + +export type ResizeHandle = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'; + +interface AspectRatioOption { + value: AspectRatio; + labelKey?: 'Free' | 'Original' | 'Square'; + label?: string; + ratio?: number; +} + +export const ASPECT_RATIOS: AspectRatioOption[] = [ + { value: 'free', labelKey: 'Free' }, + { value: 'original', labelKey: 'Original' }, + { value: 'square', labelKey: 'Square', ratio: 1 }, + { value: '3:2', label: '3:2', ratio: 3 / 2 }, + { value: '2:3', label: '2:3', ratio: 2 / 3 }, + { value: '4:3', label: '4:3', ratio: 4 / 3 }, + { value: '3:4', label: '3:4', ratio: 3 / 4 }, + { value: '5:4', label: '5:4', ratio: 5 / 4 }, + { value: '4:5', label: '4:5', ratio: 4 / 5 }, + { value: '16:9', label: '16:9', ratio: 16 / 9 }, + { value: '9:16', label: '9:16', ratio: 9 / 16 }, +]; + +export const DEFAULT_CROP_STATE: CropState = { + cropperX: 0, + cropperY: 0, + cropperWidth: 0, + cropperHeight: 0, + aspectRatio: 'free', + rotation: 0, + quarterTurns: 0, + flipH: false, +}; + +export interface CropAction { + type: 'crop'; + previousState: CropState; +} + +const MIN_CROP_SIZE = 50; +const MIN_ROTATION = -90; +const MAX_ROTATION = 90; + +function computeCenteredCrop(effW: number, effH: number, ratioValue: number | undefined) { + let width: number; + let height: number; + + if (!ratioValue) { + width = effW; + height = effH; + } else if (effW / effH > ratioValue) { + height = effH; + width = effH * ratioValue; + } else { + width = effW; + height = effW / ratioValue; + } + + return { + cropperX: (effW - width) / 2, + cropperY: (effH - height) / 2, + cropperWidth: width, + cropperHeight: height, + }; +} + +interface UseCropperOptions { + imageRef: React.RefObject; + displaySize: { width: number; height: number }; + getDisplayScale: () => number; + getDisplayCoordinates: (e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) => { x: number; y: number }; + onAction: (action: CropAction) => void; + cropState: CropState; + setCropState: (value: CropState | ((prev: CropState) => CropState)) => void; +} + +export default function useCropper({ + imageRef, + displaySize, + getDisplayScale, + getDisplayCoordinates, + onAction, + cropState, + setCropState, +}: UseCropperOptions) { + const cropperDragStartRef = useRef<{ + startX: number; + startY: number; + cropperX: number; + cropperY: number; + cropperWidth: number; + cropperHeight: number; + }>(); + + const cropStateRef = useRef(DEFAULT_CROP_STATE); + cropStateRef.current = cropState; + + const getAspectRatioValue = useLastCallback((ratio: AspectRatio): number | undefined => { + if (ratio === 'free') return undefined; + if (ratio === 'original' && imageRef.current) { + const { width: effW, height: effH } = getEffectiveDimensions( + imageRef.current.width, imageRef.current.height, cropStateRef.current.quarterTurns, + ); + return effW / effH; + } + const option = ASPECT_RATIOS.find((r) => r.value === ratio); + return option?.ratio; + }); + + const setupDragListeners = ( + onMove: (ev: MouseEvent | TouchEvent) => void, + onUp: () => void, + ) => { + const handleUp = () => { + onUp(); + document.removeEventListener('mousemove', onMove); + document.removeEventListener('touchmove', onMove); + document.removeEventListener('mouseup', handleUp); + document.removeEventListener('touchend', handleUp); + }; + + document.addEventListener('mousemove', onMove); + document.addEventListener('touchmove', onMove); + document.addEventListener('mouseup', handleUp); + document.addEventListener('touchend', handleUp); + }; + + const handleCropperDragStart = useLastCallback((e: React.MouseEvent | React.TouchEvent) => { + const img = imageRef.current; + if (!img || displaySize.width === 0) return; + + e.preventDefault(); + e.stopPropagation(); + + const { x, y } = getDisplayCoordinates(e); + const displayScale = getDisplayScale(); + + cropperDragStartRef.current = { + startX: x, + startY: y, + cropperX: cropState.cropperX, + cropperY: cropState.cropperY, + cropperWidth: cropState.cropperWidth, + cropperHeight: cropState.cropperHeight, + }; + + const handleMove = (ev: MouseEvent | TouchEvent) => { + if (!cropperDragStartRef.current) return; + + const coords = getDisplayCoordinates(ev); + const displayDeltaX = coords.x - cropperDragStartRef.current.startX; + const displayDeltaY = coords.y - cropperDragStartRef.current.startY; + + const imageDeltaX = displayDeltaX / displayScale; + const imageDeltaY = displayDeltaY / displayScale; + + const newCropperX = cropperDragStartRef.current.cropperX + imageDeltaX; + const newCropperY = cropperDragStartRef.current.cropperY + imageDeltaY; + + const { width: effW, height: effH } = getEffectiveDimensions( + img.width, img.height, cropStateRef.current.quarterTurns, + ); + const constrainedX = clamp(newCropperX, 0, effW - cropperDragStartRef.current.cropperWidth); + const constrainedY = clamp(newCropperY, 0, effH - cropperDragStartRef.current.cropperHeight); + + setCropState((prev) => ({ + ...prev, + cropperX: constrainedX, + cropperY: constrainedY, + })); + }; + + const handleUp = () => { + if (cropperDragStartRef.current) { + const startState = cropperDragStartRef.current; + if (startState.cropperX !== cropStateRef.current.cropperX + || startState.cropperY !== cropStateRef.current.cropperY) { + const previousState: CropState = { + ...cropStateRef.current, + cropperX: startState.cropperX, + cropperY: startState.cropperY, + cropperWidth: startState.cropperWidth, + cropperHeight: startState.cropperHeight, + }; + onAction({ type: 'crop', previousState }); + } + } + cropperDragStartRef.current = undefined; + }; + + setupDragListeners(handleMove, handleUp); + }); + + const handleCornerResizeStart = useLastCallback((e: React.MouseEvent | React.TouchEvent, handle: ResizeHandle) => { + const img = imageRef.current; + if (!img || displaySize.width === 0) return; + + e.preventDefault(); + e.stopPropagation(); + + const { x, y } = getDisplayCoordinates(e); + const displayScale = getDisplayScale(); + + cropperDragStartRef.current = { + startX: x, + startY: y, + cropperX: cropState.cropperX, + cropperY: cropState.cropperY, + cropperWidth: cropState.cropperWidth, + cropperHeight: cropState.cropperHeight, + }; + + const handleMove = (ev: MouseEvent | TouchEvent) => { + if (!cropperDragStartRef.current) return; + + const coords = getDisplayCoordinates(ev); + const displayDeltaX = coords.x - cropperDragStartRef.current.startX; + const displayDeltaY = coords.y - cropperDragStartRef.current.startY; + + const imageDeltaX = displayDeltaX / displayScale; + const imageDeltaY = displayDeltaY / displayScale; + + const startState = cropperDragStartRef.current; + const { width: effW, height: effH } = getEffectiveDimensions( + img.width, img.height, cropStateRef.current.quarterTurns, + ); + let newX = startState.cropperX; + let newY = startState.cropperY; + let newWidth = startState.cropperWidth; + let newHeight = startState.cropperHeight; + + const ratioValue = getAspectRatioValue(cropStateRef.current.aspectRatio); + + if (handle === 'topLeft') { + newX = startState.cropperX + imageDeltaX; + newY = startState.cropperY + imageDeltaY; + newWidth = startState.cropperWidth - imageDeltaX; + newHeight = startState.cropperHeight - imageDeltaY; + } else if (handle === 'topRight') { + newY = startState.cropperY + imageDeltaY; + newWidth = startState.cropperWidth + imageDeltaX; + newHeight = startState.cropperHeight - imageDeltaY; + } else if (handle === 'bottomLeft') { + newX = startState.cropperX + imageDeltaX; + newWidth = startState.cropperWidth - imageDeltaX; + newHeight = startState.cropperHeight + imageDeltaY; + } else if (handle === 'bottomRight') { + newWidth = startState.cropperWidth + imageDeltaX; + newHeight = startState.cropperHeight + imageDeltaY; + } + + if (ratioValue) { + const currentRatio = newWidth / newHeight; + if (currentRatio > ratioValue) { + const adjustedWidth = newHeight * ratioValue; + if (handle === 'topLeft' || handle === 'bottomLeft') { + newX += (newWidth - adjustedWidth); + } + newWidth = adjustedWidth; + } else { + const adjustedHeight = newWidth / ratioValue; + if (handle === 'topLeft' || handle === 'topRight') { + newY += (newHeight - adjustedHeight); + } + newHeight = adjustedHeight; + } + } + + if (newWidth < MIN_CROP_SIZE) { + if (handle === 'topLeft' || handle === 'bottomLeft') { + newX -= (MIN_CROP_SIZE - newWidth); + } + newWidth = MIN_CROP_SIZE; + if (ratioValue) newHeight = MIN_CROP_SIZE / ratioValue; + } + if (newHeight < MIN_CROP_SIZE) { + if (handle === 'topLeft' || handle === 'topRight') { + newY -= (MIN_CROP_SIZE - newHeight); + } + newHeight = MIN_CROP_SIZE; + if (ratioValue) newWidth = MIN_CROP_SIZE * ratioValue; + } + + // Clamp to image bounds, keeping the opposite edge fixed + const rightEdge = newX + newWidth; + const bottomEdge = newY + newHeight; + + if (handle === 'topLeft' || handle === 'bottomLeft') { + newX = Math.max(0, newX); + newWidth = rightEdge - newX; + } else { + newWidth = Math.min(newWidth, effW - newX); + } + + if (handle === 'topLeft' || handle === 'topRight') { + newY = Math.max(0, newY); + newHeight = bottomEdge - newY; + } else { + newHeight = Math.min(newHeight, effH - newY); + } + + setCropState((prev) => ({ + ...prev, + cropperX: newX, + cropperY: newY, + cropperWidth: newWidth, + cropperHeight: newHeight, + })); + }; + + const handleUp = () => { + if (cropperDragStartRef.current) { + const startState = cropperDragStartRef.current; + if (startState.cropperX !== cropStateRef.current.cropperX + || startState.cropperY !== cropStateRef.current.cropperY + || startState.cropperWidth !== cropStateRef.current.cropperWidth + || startState.cropperHeight !== cropStateRef.current.cropperHeight) { + const previousState: CropState = { + ...cropStateRef.current, + cropperX: startState.cropperX, + cropperY: startState.cropperY, + cropperWidth: startState.cropperWidth, + cropperHeight: startState.cropperHeight, + }; + onAction({ type: 'crop', previousState }); + } + } + cropperDragStartRef.current = undefined; + }; + + setupDragListeners(handleMove, handleUp); + }); + + const handleAspectRatioChange = useLastCallback((newRatio: AspectRatio) => { + const img = imageRef.current; + if (!img) return; + + const previousState = { ...cropStateRef.current }; + const { width: effW, height: effH } = getEffectiveDimensions( + img.width, img.height, cropStateRef.current.quarterTurns, + ); + + setCropState({ + ...cropStateRef.current, + aspectRatio: newRatio, + ...computeCenteredCrop(effW, effH, getAspectRatioValue(newRatio)), + }); + onAction({ type: 'crop', previousState }); + }); + + const initCropState = useLastCallback((width: number, height: number) => { + setCropState({ + aspectRatio: 'free', + cropperX: 0, + cropperY: 0, + cropperWidth: width, + cropperHeight: height, + rotation: 0, + quarterTurns: 0, + flipH: false, + }); + }); + + const getCroppedRegion = useLastCallback(() => { + const { cropperX, cropperY, cropperWidth, cropperHeight } = cropStateRef.current; + return { + x: cropperX, + y: cropperY, + width: cropperWidth, + height: cropperHeight, + }; + }); + + const rotationStartStateRef = useRef(); + + const handleRotationChange = useLastCallback((value: number) => { + const img = imageRef.current; + if (!img) return; + + if (!rotationStartStateRef.current) { + rotationStartStateRef.current = { ...cropStateRef.current }; + } + + const { width: effW, height: effH } = getEffectiveDimensions( + img.width, img.height, cropStateRef.current.quarterTurns, + ); + + setCropState({ + ...cropStateRef.current, + rotation: clamp(value, MIN_ROTATION, MAX_ROTATION), + ...computeCenteredCrop(effW, effH, getAspectRatioValue(cropStateRef.current.aspectRatio)), + }); + }); + + const handleRotationChangeEnd = useLastCallback(() => { + if (rotationStartStateRef.current) { + onAction({ type: 'crop', previousState: rotationStartStateRef.current }); + rotationStartStateRef.current = undefined; + } + }); + + const handleQuarterRotate = useLastCallback(() => { + const img = imageRef.current; + if (!img) return; + + const previousState = { ...cropStateRef.current }; + const newQuarterTurns = (cropStateRef.current.quarterTurns + 1) % 4; + const { width: newEffW, height: newEffH } = getEffectiveDimensions( + img.width, img.height, newQuarterTurns, + ); + + setCropState({ + ...cropStateRef.current, + quarterTurns: newQuarterTurns, + rotation: 0, + ...computeCenteredCrop(newEffW, newEffH, getAspectRatioValue(cropStateRef.current.aspectRatio)), + }); + onAction({ type: 'crop', previousState }); + }); + + const handleFlip = useLastCallback(() => { + const img = imageRef.current; + if (!img) return; + + const previousState = { ...cropStateRef.current }; + const { width: effW } = getEffectiveDimensions( + img.width, img.height, cropStateRef.current.quarterTurns, + ); + + setCropState({ + ...cropStateRef.current, + flipH: !cropStateRef.current.flipH, + cropperX: effW - cropStateRef.current.cropperX - cropStateRef.current.cropperWidth, + }); + onAction({ type: 'crop', previousState }); + }); + + return { + getCroppedRegion, + initCropState, + handleCropperDragStart, + handleCornerResizeStart, + handleAspectRatioChange, + handleRotationChange, + handleRotationChangeEnd, + handleQuarterRotate, + handleFlip, + }; +} diff --git a/src/components/ui/mediaEditor/hooks/useDisplaySize.ts b/src/components/ui/mediaEditor/hooks/useDisplaySize.ts new file mode 100644 index 000000000..31f4a5dc4 --- /dev/null +++ b/src/components/ui/mediaEditor/hooks/useDisplaySize.ts @@ -0,0 +1,71 @@ +import type { ElementRef } from '@teact'; +import { useEffect, useState } from '@teact'; + +import useLastCallback from '../../../../hooks/useLastCallback'; + +interface UseDisplaySizeOptions { + canvasAreaRef: ElementRef; + imageWidth: number; + imageHeight: number; + reservedHeight?: number; +} + +export default function useDisplaySize({ + canvasAreaRef, + imageWidth, + imageHeight, + reservedHeight = 0, +}: UseDisplaySizeOptions) { + const [displaySize, setDisplaySize] = useState({ width: 0, height: 0 }); + + const getDisplayScale = useLastCallback(() => { + if (displaySize.width === 0 || imageWidth === 0) return 1; + return Math.min( + displaySize.width / imageWidth, + displaySize.height / imageHeight, + ); + }); + + useEffect(() => { + const canvasArea = canvasAreaRef.current; + if (!canvasArea || imageWidth === 0) return undefined; + + const updateDisplaySize = () => { + const areaRect = canvasArea.getBoundingClientRect(); + const style = getComputedStyle(canvasArea); + const paddingX = parseFloat(style.paddingLeft) + parseFloat(style.paddingRight); + const paddingY = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom); + const availableWidth = areaRect.width - paddingX; + const availableHeight = areaRect.height - paddingY - reservedHeight; + + if (availableWidth <= 0 || availableHeight <= 0) return; + + const scaleToFit = Math.min( + availableWidth / imageWidth, + availableHeight / imageHeight, + ); + + const scale = Math.min(scaleToFit, 1); + + setDisplaySize({ + width: imageWidth * scale, + height: imageHeight * scale, + }); + }; + + updateDisplaySize(); + + window.addEventListener('resize', updateDisplaySize); + return () => window.removeEventListener('resize', updateDisplaySize); + }, [canvasAreaRef, imageWidth, imageHeight, reservedHeight]); + + const resetDisplaySize = useLastCallback(() => { + setDisplaySize({ width: 0, height: 0 }); + }); + + return { + displaySize, + getDisplayScale, + resetDisplaySize, + }; +} diff --git a/src/components/ui/mediaEditor/hooks/useDrawing.ts b/src/components/ui/mediaEditor/hooks/useDrawing.ts new file mode 100644 index 000000000..350945840 --- /dev/null +++ b/src/components/ui/mediaEditor/hooks/useDrawing.ts @@ -0,0 +1,112 @@ +import { useRef, useState } from '@teact'; + +import type { DrawAction, DrawTool } from '../canvasUtils'; + +import useFlag from '../../../../hooks/useFlag'; +import useLastCallback from '../../../../hooks/useLastCallback'; + +const DEFAULT_BRUSH_SIZE = 5; + +interface UseDrawingOptions { + getCanvasCoordinates: (e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) => { x: number; y: number }; + canvasToImageCoords: (x: number, y: number) => { x: number; y: number }; + selectedColor: string; + onActionComplete: (action: DrawAction) => void; +} + +export default function useDrawing({ + getCanvasCoordinates, + canvasToImageCoords, + selectedColor, + onActionComplete, +}: UseDrawingOptions) { + const [drawTool, setDrawTool] = useState('pen'); + const [brushSize, setBrushSize] = useState(DEFAULT_BRUSH_SIZE); + const [currentDrawAction, setCurrentDrawAction] = useState(undefined); + const [isDrawing, markDrawing, unmarkDrawing] = useFlag(false); + const lastCompletedActionRef = useRef(undefined); + + const handlePointerMove = useLastCallback((e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) => { + // Also check lastCompletedActionRef to prevent moves after completion (stale state race) + if (!isDrawing || !currentDrawAction || lastCompletedActionRef.current === currentDrawAction) return; + + const canvasCoords = getCanvasCoordinates(e); + const imageCoords = canvasToImageCoords(canvasCoords.x, canvasCoords.y); + const isShiftPressed = 'shiftKey' in e ? e.shiftKey : false; + + // When shift is pressed, only keep first and last point (straight line) + const newPoints = isShiftPressed + ? [currentDrawAction.points[0], imageCoords] + : [...currentDrawAction.points, imageCoords]; + + setCurrentDrawAction({ + ...currentDrawAction, + points: newPoints, + isShiftPressed, + }); + }); + + const handlePointerUp = useLastCallback((e?: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) => { + // Use ref to prevent double completion from mouseup + mouseleave firing together + if (!isDrawing || !currentDrawAction || lastCompletedActionRef.current === currentDrawAction) return; + + unmarkDrawing(); + const completedAction = { + ...currentDrawAction, + completedAt: Date.now(), + }; + lastCompletedActionRef.current = completedAction; + setCurrentDrawAction(undefined); + if (completedAction.points.length > 1) { + onActionComplete(completedAction); + } + + document.removeEventListener('mousemove', handlePointerMove); + document.removeEventListener('touchmove', handlePointerMove); + document.removeEventListener('mouseup', handlePointerUp); + document.removeEventListener('touchend', handlePointerUp); + }); + + const handlePointerDown = useLastCallback((e: React.MouseEvent | React.TouchEvent) => { + markDrawing(); + const canvasCoords = getCanvasCoordinates(e); + const imageCoords = canvasToImageCoords(canvasCoords.x, canvasCoords.y); + const isShiftPressed = 'shiftKey' in e ? e.shiftKey : false; + + setCurrentDrawAction({ + type: 'draw', + tool: drawTool, + points: [imageCoords], + color: selectedColor, + brushSize, + isShiftPressed, + }); + + // Attach document listeners to continue drawing even when cursor leaves canvas + document.addEventListener('mousemove', handlePointerMove); + document.addEventListener('touchmove', handlePointerMove); + document.addEventListener('mouseup', handlePointerUp); + document.addEventListener('touchend', handlePointerUp); + }); + + const resetDrawing = useLastCallback(() => { + setCurrentDrawAction(undefined); + unmarkDrawing(); + }); + + return { + drawTool, + setDrawTool, + brushSize, + setBrushSize, + currentDrawAction, + isDrawing, + handlePointerDown, + handlePointerMove, + handlePointerUp, + resetDrawing, + }; +} + +export const MIN_BRUSH_SIZE = 2; +export const MAX_BRUSH_SIZE = 50; diff --git a/src/global/actions/ui/misc.ts b/src/global/actions/ui/misc.ts index 64ac3d2c9..316612e7f 100644 --- a/src/global/actions/ui/misc.ts +++ b/src/global/actions/ui/misc.ts @@ -1,7 +1,7 @@ import { addCallback } from '../../../lib/teact/teactn'; -import type { ApiError } from '../../../api/types'; import type { ActionReturnType, GlobalState } from '../../types'; +import { type ApiError } from '../../../api/types'; import { ANIMATION_WAVE_MIN_INTERVAL, @@ -19,7 +19,7 @@ import { refreshFromCache } from '../../../util/localization'; import * as langProvider from '../../../util/oldLangProvider'; import updateIcon from '../../../util/updateIcon'; import { setPageTitle, setPageTitleInstant } from '../../../util/updatePageTitle'; -import { getAllowedAttachmentOptions, getChatTitle } from '../../helpers'; +import { canEditMediaInEditor, getAllowedAttachmentOptions, getChatTitle } from '../../helpers'; import { addTabStateResetterAction } from '../../helpers/meta'; import { addActionHandler, getActions, getGlobal, setGlobal, @@ -42,6 +42,7 @@ import { selectTopic, } from '../../selectors'; import { selectSharedSettings } from '../../selectors/sharedState'; +import { selectDraft, selectEditingId } from '../../selectors/threads'; import { getIsMobile, getIsTablet } from '../../../hooks/useAppLayout'; @@ -993,3 +994,25 @@ addActionHandler('openCocoonModal', (global, actions, payload): ActionReturnType }); addTabStateResetterAction('closeCocoonModal', 'isCocoonModalOpen'); + +addActionHandler('requestMessageMediaEditor', (global, actions, payload): ActionReturnType => { + const { tabId = getCurrentTabId() } = payload || {}; + const currentMessageList = selectCurrentMessageList(global, tabId); + if (!currentMessageList) return; + + const draft = selectDraft(global, currentMessageList.chatId, currentMessageList.threadId); + const replyToMessage = draft?.replyInfo + ? selectChatMessage(global, currentMessageList.chatId, draft.replyInfo.replyToMsgId) + : undefined; + const editingId = selectEditingId(global, currentMessageList.chatId, currentMessageList.threadId); + const editingMessage = editingId ? selectChatMessage(global, currentMessageList.chatId, editingId) : undefined; + + const message = replyToMessage || editingMessage; + if (!message || !canEditMediaInEditor(message)) return; + + return updateTabState(global, { + shouldOpenMessageMediaEditor: true, + }, tabId); +}); + +addTabStateResetterAction('resetMessageMediaEditorRequest', 'shouldOpenMessageMediaEditor'); diff --git a/src/global/helpers/messageMedia.ts b/src/global/helpers/messageMedia.ts index 7b5f1736d..f34b2c1dc 100644 --- a/src/global/helpers/messageMedia.ts +++ b/src/global/helpers/messageMedia.ts @@ -61,6 +61,10 @@ export function canEditMedia(message: MediaContainer) { return !video?.isRound && !Object.keys(otherMedia).length; } +export function canEditMediaInEditor(message: MediaContainer) { + return canEditMedia(message) && (getMessagePhoto(message) || getMessageDocumentPhoto(message)); +} + export function getMessagePhoto(message: MediaContainer) { return message.content.photo; } diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts index 306a8a2a3..8d27ecb30 100644 --- a/src/global/types/actions.ts +++ b/src/global/types/actions.ts @@ -3060,6 +3060,9 @@ export interface ActionPayloads { openCocoonModal: WithTabId | undefined; closeCocoonModal: WithTabId | undefined; + + requestMessageMediaEditor: WithTabId | undefined; + resetMessageMediaEditorRequest: WithTabId | undefined; } export interface RequiredActionPayloads { diff --git a/src/global/types/tabState.ts b/src/global/types/tabState.ts index afc4e73d0..181e63f45 100644 --- a/src/global/types/tabState.ts +++ b/src/global/types/tabState.ts @@ -1029,4 +1029,6 @@ export type TabState = { shouldSaveAttachmentsCompression?: boolean; isCocoonModalOpen?: boolean; + + shouldOpenMessageMediaEditor?: boolean; }; diff --git a/src/styles/icons.css b/src/styles/icons.css index 83cf54e09..38f2e1352 100644 --- a/src/styles/icons.css +++ b/src/styles/icons.css @@ -3,8 +3,8 @@ font-weight: normal; font-style: normal; font-display: block; - src: url("./icons.woff2?9bd1ca23f0a305a032d8f27f3b31a6cc") format("woff2"), -url("./icons.woff?9bd1ca23f0a305a032d8f27f3b31a6cc") format("woff"); + src: url("./icons.woff2?d1a9cacb64e401206f928a39a587a2bd") format("woff2"), +url("./icons.woff?d1a9cacb64e401206f928a39a587a2bd") format("woff"); } .icon-char::before { @@ -102,867 +102,885 @@ url("./icons.woff?9bd1ca23f0a305a032d8f27f3b31a6cc") format("woff"); .icon-unique-profile::before { content: "\f11b"; } -.icon-understood::before { +.icon-undo::before { content: "\f11c"; } -.icon-underlined::before { +.icon-understood::before { content: "\f11d"; } -.icon-unarchive::before { +.icon-underlined::before { content: "\f11e"; } -.icon-truck::before { +.icon-unarchive::before { content: "\f11f"; } -.icon-transcribe::before { +.icon-truck::before { content: "\f120"; } -.icon-trade::before { +.icon-transcribe::before { content: "\f121"; } -.icon-topic-new::before { +.icon-trade::before { content: "\f122"; } -.icon-tools::before { +.icon-topic-new::before { content: "\f123"; } -.icon-toncoin::before { +.icon-tools::before { content: "\f124"; } -.icon-timer::before { +.icon-toncoin::before { content: "\f125"; } -.icon-tag::before { +.icon-timer::before { content: "\f126"; } -.icon-tag-name::before { +.icon-tag::before { content: "\f127"; } -.icon-tag-filter::before { +.icon-tag-name::before { content: "\f128"; } -.icon-tag-crossed::before { +.icon-tag-filter::before { content: "\f129"; } -.icon-tag-add::before { +.icon-tag-crossed::before { content: "\f12a"; } -.icon-strikethrough::before { +.icon-tag-add::before { content: "\f12b"; } -.icon-story-reply::before { +.icon-strikethrough::before { content: "\f12c"; } -.icon-story-priority::before { +.icon-story-reply::before { content: "\f12d"; } -.icon-story-expired::before { +.icon-story-priority::before { content: "\f12e"; } -.icon-story-caption::before { +.icon-story-expired::before { content: "\f12f"; } -.icon-stop::before { +.icon-story-caption::before { content: "\f130"; } -.icon-stop-raising-hand::before { +.icon-stop::before { content: "\f131"; } -.icon-stickers::before { +.icon-stop-raising-hand::before { content: "\f132"; } -.icon-stealth-past::before { +.icon-stickers::before { content: "\f133"; } -.icon-stealth-future::before { +.icon-stealth-past::before { content: "\f134"; } -.icon-stats::before { +.icon-stealth-future::before { content: "\f135"; } -.icon-stars-refund::before { +.icon-stats::before { content: "\f136"; } -.icon-stars-lock::before { +.icon-stars-refund::before { content: "\f137"; } -.icon-star::before { +.icon-stars-lock::before { content: "\f138"; } -.icon-sport::before { +.icon-star::before { content: "\f139"; } -.icon-spoiler::before { +.icon-sport::before { content: "\f13a"; } -.icon-spoiler-disable::before { +.icon-spoiler::before { content: "\f13b"; } -.icon-speaker::before { +.icon-spoiler-disable::before { content: "\f13c"; } -.icon-speaker-story::before { +.icon-speaker::before { content: "\f13d"; } -.icon-speaker-outline::before { +.icon-speaker-story::before { content: "\f13e"; } -.icon-speaker-muted-story::before { +.icon-speaker-outline::before { content: "\f13f"; } -.icon-sort::before { +.icon-speaker-muted-story::before { content: "\f140"; } -.icon-sort-by-price::before { +.icon-sort::before { content: "\f141"; } -.icon-sort-by-number::before { +.icon-sort-by-price::before { content: "\f142"; } -.icon-sort-by-date::before { +.icon-sort-by-number::before { content: "\f143"; } -.icon-smile::before { +.icon-sort-by-date::before { content: "\f144"; } -.icon-smallscreen::before { +.icon-smile::before { content: "\f145"; } -.icon-skip-previous::before { +.icon-smallscreen::before { content: "\f146"; } -.icon-skip-next::before { +.icon-skip-previous::before { content: "\f147"; } -.icon-sidebar::before { +.icon-skip-next::before { content: "\f148"; } -.icon-show-message::before { +.icon-sidebar::before { content: "\f149"; } -.icon-share-screen::before { +.icon-show-message::before { content: "\f14a"; } -.icon-share-screen-stop::before { +.icon-share-screen::before { content: "\f14b"; } -.icon-share-screen-outlined::before { +.icon-share-screen-stop::before { content: "\f14c"; } -.icon-share-filled::before { +.icon-share-screen-outlined::before { content: "\f14d"; } -.icon-settings::before { +.icon-share-filled::before { content: "\f14e"; } -.icon-settings-filled::before { +.icon-settings::before { content: "\f14f"; } -.icon-send::before { +.icon-settings-filled::before { content: "\f150"; } -.icon-send-outline::before { +.icon-send::before { content: "\f151"; } -.icon-sell::before { +.icon-send-outline::before { content: "\f152"; } -.icon-sell-outline::before { +.icon-sell::before { content: "\f153"; } -.icon-select::before { +.icon-sell-outline::before { content: "\f154"; } -.icon-search::before { +.icon-select::before { content: "\f155"; } -.icon-sd-photo::before { +.icon-search::before { content: "\f156"; } -.icon-scheduled::before { +.icon-sd-photo::before { content: "\f157"; } -.icon-schedule::before { +.icon-scheduled::before { content: "\f158"; } -.icon-saved-messages::before { +.icon-schedule::before { content: "\f159"; } -.icon-save-story::before { +.icon-saved-messages::before { content: "\f15a"; } -.icon-revote::before { +.icon-save-story::before { content: "\f15b"; } -.icon-revenue-split::before { +.icon-rotate::before { content: "\f15c"; } -.icon-reply::before { +.icon-revote::before { content: "\f15d"; } -.icon-reply-filled::before { +.icon-revenue-split::before { content: "\f15e"; } -.icon-replies::before { +.icon-reply::before { content: "\f15f"; } -.icon-replace::before { +.icon-reply-filled::before { content: "\f160"; } -.icon-reorder-tabs::before { +.icon-replies::before { content: "\f161"; } -.icon-reopen-topic::before { +.icon-replace::before { content: "\f162"; } -.icon-remove::before { +.icon-reorder-tabs::before { content: "\f163"; } -.icon-remove-quote::before { +.icon-reopen-topic::before { content: "\f164"; } -.icon-reload::before { +.icon-remove::before { content: "\f165"; } -.icon-refund::before { +.icon-remove-quote::before { content: "\f166"; } -.icon-recent::before { +.icon-reload::before { content: "\f167"; } -.icon-readchats::before { +.icon-refund::before { content: "\f168"; } -.icon-radial-badge::before { +.icon-redo::before { content: "\f169"; } -.icon-quote::before { +.icon-recent::before { content: "\f16a"; } -.icon-quote-text::before { +.icon-readchats::before { content: "\f16b"; } -.icon-proof-of-ownership::before { +.icon-radial-badge::before { content: "\f16c"; } -.icon-privacy-policy::before { +.icon-quote::before { content: "\f16d"; } -.icon-previous::before { +.icon-quote-text::before { content: "\f16e"; } -.icon-poll::before { +.icon-proof-of-ownership::before { content: "\f16f"; } -.icon-play::before { +.icon-privacy-policy::before { content: "\f170"; } -.icon-play-story::before { +.icon-previous::before { content: "\f171"; } -.icon-pip::before { +.icon-poll::before { content: "\f172"; } -.icon-pinned-message::before { +.icon-play::before { content: "\f173"; } -.icon-pinned-chat::before { +.icon-play-story::before { content: "\f174"; } -.icon-pin::before { +.icon-pip::before { content: "\f175"; } -.icon-pin-list::before { +.icon-pinned-message::before { content: "\f176"; } -.icon-pin-badge::before { +.icon-pinned-chat::before { content: "\f177"; } -.icon-photo::before { +.icon-pin::before { content: "\f178"; } -.icon-phone::before { +.icon-pin-list::before { content: "\f179"; } -.icon-phone-discard::before { +.icon-pin-badge::before { content: "\f17a"; } -.icon-phone-discard-outline::before { +.icon-photo::before { content: "\f17b"; } -.icon-permissions::before { +.icon-phone::before { content: "\f17c"; } -.icon-pause::before { +.icon-phone-discard::before { content: "\f17d"; } -.icon-password-off::before { +.icon-phone-discard-outline::before { content: "\f17e"; } -.icon-open-in-new-tab::before { +.icon-permissions::before { content: "\f17f"; } -.icon-one-filled::before { +.icon-pause::before { content: "\f180"; } -.icon-note::before { +.icon-password-off::before { content: "\f181"; } -.icon-non-contacts::before { +.icon-open-in-new-tab::before { content: "\f182"; } -.icon-noise-suppression::before { +.icon-one-filled::before { content: "\f183"; } -.icon-nochannel::before { +.icon-note::before { content: "\f184"; } -.icon-next::before { +.icon-non-contacts::before { content: "\f185"; } -.icon-next-link::before { +.icon-noise-suppression::before { content: "\f186"; } -.icon-new-chat-filled::before { +.icon-nochannel::before { content: "\f187"; } -.icon-my-notes::before { +.icon-next::before { content: "\f188"; } -.icon-muted::before { +.icon-next-link::before { content: "\f189"; } -.icon-mute::before { +.icon-new-chat-filled::before { content: "\f18a"; } -.icon-move-caption-up::before { +.icon-my-notes::before { content: "\f18b"; } -.icon-move-caption-down::before { +.icon-muted::before { content: "\f18c"; } -.icon-more::before { +.icon-mute::before { content: "\f18d"; } -.icon-more-circle::before { +.icon-move-caption-up::before { content: "\f18e"; } -.icon-monospace::before { +.icon-move-caption-down::before { content: "\f18f"; } -.icon-microphone::before { +.icon-more::before { content: "\f190"; } -.icon-microphone-alt::before { +.icon-more-circle::before { content: "\f191"; } -.icon-message::before { +.icon-monospace::before { content: "\f192"; } -.icon-message-succeeded::before { +.icon-microphone::before { content: "\f193"; } -.icon-message-read::before { +.icon-microphone-alt::before { content: "\f194"; } -.icon-message-pending::before { +.icon-message::before { content: "\f195"; } -.icon-message-failed::before { +.icon-message-succeeded::before { content: "\f196"; } -.icon-menu::before { +.icon-message-read::before { content: "\f197"; } -.icon-mention::before { +.icon-message-pending::before { content: "\f198"; } -.icon-loop::before { +.icon-message-failed::before { content: "\f199"; } -.icon-logout::before { +.icon-menu::before { content: "\f19a"; } -.icon-lock::before { +.icon-mention::before { content: "\f19b"; } -.icon-lock-badge::before { +.icon-loop::before { content: "\f19c"; } -.icon-location::before { +.icon-logout::before { content: "\f19d"; } -.icon-link::before { +.icon-lock::before { content: "\f19e"; } -.icon-link-broken::before { +.icon-lock-badge::before { content: "\f19f"; } -.icon-link-badge::before { +.icon-location::before { content: "\f1a0"; } -.icon-large-play::before { +.icon-link::before { content: "\f1a1"; } -.icon-large-pause::before { +.icon-link-broken::before { content: "\f1a2"; } -.icon-language::before { +.icon-link-badge::before { content: "\f1a3"; } -.icon-lamp::before { +.icon-large-play::before { content: "\f1a4"; } -.icon-keyboard::before { +.icon-large-pause::before { content: "\f1a5"; } -.icon-key::before { +.icon-language::before { content: "\f1a6"; } -.icon-italic::before { +.icon-lamp::before { content: "\f1a7"; } -.icon-install::before { +.icon-keyboard::before { content: "\f1a8"; } -.icon-info::before { +.icon-key::before { content: "\f1a9"; } -.icon-info-filled::before { +.icon-italic::before { content: "\f1aa"; } -.icon-help::before { +.icon-install::before { content: "\f1ab"; } -.icon-heart::before { +.icon-info::before { content: "\f1ac"; } -.icon-heart-outline::before { +.icon-info-filled::before { content: "\f1ad"; } -.icon-hd-photo::before { +.icon-help::before { content: "\f1ae"; } -.icon-hashtag::before { +.icon-heart::before { content: "\f1af"; } -.icon-hand-stop::before { +.icon-heart-outline::before { content: "\f1b0"; } -.icon-grouped::before { +.icon-hd-photo::before { content: "\f1b1"; } -.icon-grouped-disable::before { +.icon-hashtag::before { content: "\f1b2"; } -.icon-group::before { +.icon-hand-stop::before { content: "\f1b3"; } -.icon-group-filled::before { +.icon-grouped::before { content: "\f1b4"; } -.icon-gift::before { +.icon-grouped-disable::before { content: "\f1b5"; } -.icon-gift-transfer-inline::before { +.icon-group::before { content: "\f1b6"; } -.icon-gifs::before { +.icon-group-filled::before { content: "\f1b7"; } -.icon-fullscreen::before { +.icon-gift::before { content: "\f1b8"; } -.icon-frozen-time::before { +.icon-gift-transfer-inline::before { content: "\f1b9"; } -.icon-fragment::before { +.icon-gifs::before { content: "\f1ba"; } -.icon-forward::before { +.icon-fullscreen::before { content: "\f1bb"; } -.icon-forums::before { +.icon-frozen-time::before { content: "\f1bc"; } -.icon-fontsize::before { +.icon-fragment::before { content: "\f1bd"; } -.icon-folder::before { +.icon-forward::before { content: "\f1be"; } -.icon-folder-badge::before { +.icon-forums::before { content: "\f1bf"; } -.icon-flag::before { +.icon-fontsize::before { content: "\f1c0"; } -.icon-file-badge::before { +.icon-folder::before { content: "\f1c1"; } -.icon-favorite::before { +.icon-folder-badge::before { content: "\f1c2"; } -.icon-favorite-filled::before { +.icon-flip::before { content: "\f1c3"; } -.icon-eye::before { +.icon-flag::before { content: "\f1c4"; } -.icon-eye-outline::before { +.icon-file-badge::before { content: "\f1c5"; } -.icon-eye-crossed::before { +.icon-favorite::before { content: "\f1c6"; } -.icon-eye-crossed-outline::before { +.icon-favorite-filled::before { content: "\f1c7"; } -.icon-expand::before { +.icon-eye::before { content: "\f1c8"; } -.icon-expand-modal::before { +.icon-eye-outline::before { content: "\f1c9"; } -.icon-enter::before { +.icon-eye-crossed::before { content: "\f1ca"; } -.icon-email::before { +.icon-eye-crossed-outline::before { content: "\f1cb"; } -.icon-edit::before { +.icon-expand::before { content: "\f1cc"; } -.icon-eats::before { +.icon-expand-modal::before { content: "\f1cd"; } -.icon-dropdown-arrows::before { +.icon-enter::before { content: "\f1ce"; } -.icon-download::before { +.icon-email::before { content: "\f1cf"; } -.icon-down::before { +.icon-edit::before { content: "\f1d0"; } -.icon-double-badge::before { +.icon-eats::before { content: "\f1d1"; } -.icon-document::before { +.icon-dropdown-arrows::before { content: "\f1d2"; } -.icon-diamond::before { +.icon-download::before { content: "\f1d3"; } -.icon-delete::before { +.icon-down::before { content: "\f1d4"; } -.icon-delete-user::before { +.icon-double-badge::before { content: "\f1d5"; } -.icon-delete-left::before { +.icon-document::before { content: "\f1d6"; } -.icon-delete-filled::before { +.icon-diamond::before { content: "\f1d7"; } -.icon-data::before { +.icon-delete::before { content: "\f1d8"; } -.icon-darkmode::before { +.icon-delete-user::before { content: "\f1d9"; } -.icon-crown-wear::before { +.icon-delete-left::before { content: "\f1da"; } -.icon-crown-wear-outline::before { +.icon-delete-filled::before { content: "\f1db"; } -.icon-crown-take-off::before { +.icon-data::before { content: "\f1dc"; } -.icon-crown-take-off-outline::before { +.icon-darkmode::before { content: "\f1dd"; } -.icon-craft::before { +.icon-crown-wear::before { content: "\f1de"; } -.icon-copy::before { +.icon-crown-wear-outline::before { content: "\f1df"; } -.icon-copy-media::before { +.icon-crown-take-off::before { content: "\f1e0"; } -.icon-comments::before { +.icon-crown-take-off-outline::before { content: "\f1e1"; } -.icon-comments-sticker::before { +.icon-crop::before { content: "\f1e2"; } -.icon-combine-craft::before { +.icon-craft::before { content: "\f1e3"; } -.icon-colorize::before { +.icon-copy::before { content: "\f1e4"; } -.icon-collapse::before { +.icon-copy-media::before { content: "\f1e5"; } -.icon-collapse-modal::before { +.icon-comments::before { content: "\f1e6"; } -.icon-cloud-download::before { +.icon-comments-sticker::before { content: "\f1e7"; } -.icon-closed-gift::before { +.icon-combine-craft::before { content: "\f1e8"; } -.icon-close::before { +.icon-colorize::before { content: "\f1e9"; } -.icon-close-topic::before { +.icon-collapse::before { content: "\f1ea"; } -.icon-close-circle::before { +.icon-collapse-modal::before { content: "\f1eb"; } -.icon-clock::before { +.icon-cloud-download::before { content: "\f1ec"; } -.icon-clock-edit::before { +.icon-closed-gift::before { content: "\f1ed"; } -.icon-check::before { +.icon-close::before { content: "\f1ee"; } -.icon-chats-badge::before { +.icon-close-topic::before { content: "\f1ef"; } -.icon-chat-badge::before { +.icon-close-circle::before { content: "\f1f0"; } -.icon-channelviews::before { +.icon-clock::before { content: "\f1f1"; } -.icon-channel::before { +.icon-clock-edit::before { content: "\f1f2"; } -.icon-channel-filled::before { +.icon-check::before { content: "\f1f3"; } -.icon-cash-circle::before { +.icon-chats-badge::before { content: "\f1f4"; } -.icon-card::before { +.icon-chat-badge::before { content: "\f1f5"; } -.icon-car::before { +.icon-channelviews::before { content: "\f1f6"; } -.icon-camera::before { +.icon-channel::before { content: "\f1f7"; } -.icon-camera-add::before { +.icon-channel-filled::before { content: "\f1f8"; } -.icon-calendar::before { +.icon-cash-circle::before { content: "\f1f9"; } -.icon-calendar-filter::before { +.icon-card::before { content: "\f1fa"; } -.icon-bug::before { +.icon-car::before { content: "\f1fb"; } -.icon-bots::before { +.icon-camera::before { content: "\f1fc"; } -.icon-bot-commands-filled::before { +.icon-camera-add::before { content: "\f1fd"; } -.icon-bot-command::before { +.icon-calendar::before { content: "\f1fe"; } -.icon-boosts::before { +.icon-calendar-filter::before { content: "\f1ff"; } -.icon-boostcircle::before { +.icon-bug::before { content: "\f200"; } -.icon-boost::before { +.icon-brush::before { content: "\f201"; } -.icon-boost-outline::before { +.icon-bots::before { content: "\f202"; } -.icon-boost-craft-chance::before { +.icon-bot-commands-filled::before { content: "\f203"; } -.icon-bold::before { +.icon-bot-command::before { content: "\f204"; } -.icon-avatar-saved-messages::before { +.icon-boosts::before { content: "\f205"; } -.icon-avatar-deleted-account::before { +.icon-boostcircle::before { content: "\f206"; } -.icon-avatar-archived-chats::before { +.icon-boost::before { content: "\f207"; } -.icon-author-hidden::before { +.icon-boost-outline::before { content: "\f208"; } -.icon-auction::before { +.icon-boost-craft-chance::before { content: "\f209"; } -.icon-auction-next-round::before { +.icon-bold::before { content: "\f20a"; } -.icon-auction-filled::before { +.icon-avatar-saved-messages::before { content: "\f20b"; } -.icon-auction-drop::before { +.icon-avatar-deleted-account::before { content: "\f20c"; } -.icon-attach::before { +.icon-avatar-archived-chats::before { content: "\f20d"; } -.icon-ask-support::before { +.icon-author-hidden::before { content: "\f20e"; } -.icon-arrow-right::before { +.icon-auction::before { content: "\f20f"; } -.icon-arrow-left::before { +.icon-auction-next-round::before { content: "\f210"; } -.icon-arrow-down::before { +.icon-auction-filled::before { content: "\f211"; } -.icon-arrow-down-circle::before { +.icon-auction-drop::before { content: "\f212"; } -.icon-archive::before { +.icon-attach::before { content: "\f213"; } -.icon-archive-to-main::before { +.icon-ask-support::before { content: "\f214"; } -.icon-archive-from-main::before { +.icon-arrow-right::before { content: "\f215"; } -.icon-archive-filled::before { +.icon-arrow-left::before { content: "\f216"; } -.icon-animations::before { +.icon-arrow-down::before { content: "\f217"; } -.icon-animals::before { +.icon-arrow-down-circle::before { content: "\f218"; } -.icon-allow-speak::before { +.icon-archive::before { content: "\f219"; } -.icon-admin::before { +.icon-archive-to-main::before { content: "\f21a"; } -.icon-add::before { +.icon-archive-from-main::before { content: "\f21b"; } -.icon-add-user::before { +.icon-archive-filled::before { content: "\f21c"; } -.icon-add-user-filled::before { +.icon-animations::before { content: "\f21d"; } -.icon-add-one-badge::before { +.icon-animals::before { content: "\f21e"; } -.icon-add-filled::before { +.icon-allow-speak::before { content: "\f21f"; } -.icon-active-sessions::before { +.icon-admin::before { content: "\f220"; } -.icon-rating-icons-negative::before { +.icon-add::before { content: "\f221"; } -.icon-rating-icons-level90::before { +.icon-add-user::before { content: "\f222"; } -.icon-rating-icons-level9::before { +.icon-add-user-filled::before { content: "\f223"; } -.icon-rating-icons-level80::before { +.icon-add-one-badge::before { content: "\f224"; } -.icon-rating-icons-level8::before { +.icon-add-filled::before { content: "\f225"; } -.icon-rating-icons-level70::before { +.icon-active-sessions::before { content: "\f226"; } -.icon-rating-icons-level7::before { +.icon-folder-tabs-user::before { content: "\f227"; } -.icon-rating-icons-level60::before { +.icon-folder-tabs-star::before { content: "\f228"; } -.icon-rating-icons-level6::before { +.icon-folder-tabs-group::before { content: "\f229"; } -.icon-rating-icons-level50::before { +.icon-folder-tabs-folder::before { content: "\f22a"; } -.icon-rating-icons-level5::before { +.icon-folder-tabs-chats::before { content: "\f22b"; } -.icon-rating-icons-level40::before { +.icon-folder-tabs-chat::before { content: "\f22c"; } -.icon-rating-icons-level4::before { +.icon-folder-tabs-channel::before { content: "\f22d"; } -.icon-rating-icons-level30::before { +.icon-folder-tabs-bot::before { content: "\f22e"; } -.icon-rating-icons-level3::before { +.icon-rating-icons-negative::before { content: "\f22f"; } -.icon-rating-icons-level20::before { +.icon-rating-icons-level90::before { content: "\f230"; } -.icon-rating-icons-level2::before { +.icon-rating-icons-level9::before { content: "\f231"; } -.icon-rating-icons-level10::before { +.icon-rating-icons-level80::before { content: "\f232"; } -.icon-rating-icons-level1::before { +.icon-rating-icons-level8::before { content: "\f233"; } -.icon-folder-tabs-user::before { +.icon-rating-icons-level70::before { content: "\f234"; } -.icon-folder-tabs-star::before { +.icon-rating-icons-level7::before { content: "\f235"; } -.icon-folder-tabs-group::before { +.icon-rating-icons-level60::before { content: "\f236"; } -.icon-folder-tabs-folder::before { +.icon-rating-icons-level6::before { content: "\f237"; } -.icon-folder-tabs-chats::before { +.icon-rating-icons-level50::before { content: "\f238"; } -.icon-folder-tabs-chat::before { +.icon-rating-icons-level5::before { content: "\f239"; } -.icon-folder-tabs-channel::before { +.icon-rating-icons-level40::before { content: "\f23a"; } -.icon-folder-tabs-bot::before { +.icon-rating-icons-level4::before { content: "\f23b"; } +.icon-rating-icons-level30::before { + content: "\f23c"; +} +.icon-rating-icons-level3::before { + content: "\f23d"; +} +.icon-rating-icons-level20::before { + content: "\f23e"; +} +.icon-rating-icons-level2::before { + content: "\f23f"; +} +.icon-rating-icons-level10::before { + content: "\f240"; +} +.icon-rating-icons-level1::before { + content: "\f241"; +} diff --git a/src/styles/icons.scss b/src/styles/icons.scss index 46f0a6232..de98a69b0 100644 --- a/src/styles/icons.scss +++ b/src/styles/icons.scss @@ -43,292 +43,298 @@ $icons-map: ( "unlist": "\f119", "unlist-outline": "\f11a", "unique-profile": "\f11b", - "understood": "\f11c", - "underlined": "\f11d", - "unarchive": "\f11e", - "truck": "\f11f", - "transcribe": "\f120", - "trade": "\f121", - "topic-new": "\f122", - "tools": "\f123", - "toncoin": "\f124", - "timer": "\f125", - "tag": "\f126", - "tag-name": "\f127", - "tag-filter": "\f128", - "tag-crossed": "\f129", - "tag-add": "\f12a", - "strikethrough": "\f12b", - "story-reply": "\f12c", - "story-priority": "\f12d", - "story-expired": "\f12e", - "story-caption": "\f12f", - "stop": "\f130", - "stop-raising-hand": "\f131", - "stickers": "\f132", - "stealth-past": "\f133", - "stealth-future": "\f134", - "stats": "\f135", - "stars-refund": "\f136", - "stars-lock": "\f137", - "star": "\f138", - "sport": "\f139", - "spoiler": "\f13a", - "spoiler-disable": "\f13b", - "speaker": "\f13c", - "speaker-story": "\f13d", - "speaker-outline": "\f13e", - "speaker-muted-story": "\f13f", - "sort": "\f140", - "sort-by-price": "\f141", - "sort-by-number": "\f142", - "sort-by-date": "\f143", - "smile": "\f144", - "smallscreen": "\f145", - "skip-previous": "\f146", - "skip-next": "\f147", - "sidebar": "\f148", - "show-message": "\f149", - "share-screen": "\f14a", - "share-screen-stop": "\f14b", - "share-screen-outlined": "\f14c", - "share-filled": "\f14d", - "settings": "\f14e", - "settings-filled": "\f14f", - "send": "\f150", - "send-outline": "\f151", - "sell": "\f152", - "sell-outline": "\f153", - "select": "\f154", - "search": "\f155", - "sd-photo": "\f156", - "scheduled": "\f157", - "schedule": "\f158", - "saved-messages": "\f159", - "save-story": "\f15a", - "revote": "\f15b", - "revenue-split": "\f15c", - "reply": "\f15d", - "reply-filled": "\f15e", - "replies": "\f15f", - "replace": "\f160", - "reorder-tabs": "\f161", - "reopen-topic": "\f162", - "remove": "\f163", - "remove-quote": "\f164", - "reload": "\f165", - "refund": "\f166", - "recent": "\f167", - "readchats": "\f168", - "radial-badge": "\f169", - "quote": "\f16a", - "quote-text": "\f16b", - "proof-of-ownership": "\f16c", - "privacy-policy": "\f16d", - "previous": "\f16e", - "poll": "\f16f", - "play": "\f170", - "play-story": "\f171", - "pip": "\f172", - "pinned-message": "\f173", - "pinned-chat": "\f174", - "pin": "\f175", - "pin-list": "\f176", - "pin-badge": "\f177", - "photo": "\f178", - "phone": "\f179", - "phone-discard": "\f17a", - "phone-discard-outline": "\f17b", - "permissions": "\f17c", - "pause": "\f17d", - "password-off": "\f17e", - "open-in-new-tab": "\f17f", - "one-filled": "\f180", - "note": "\f181", - "non-contacts": "\f182", - "noise-suppression": "\f183", - "nochannel": "\f184", - "next": "\f185", - "next-link": "\f186", - "new-chat-filled": "\f187", - "my-notes": "\f188", - "muted": "\f189", - "mute": "\f18a", - "move-caption-up": "\f18b", - "move-caption-down": "\f18c", - "more": "\f18d", - "more-circle": "\f18e", - "monospace": "\f18f", - "microphone": "\f190", - "microphone-alt": "\f191", - "message": "\f192", - "message-succeeded": "\f193", - "message-read": "\f194", - "message-pending": "\f195", - "message-failed": "\f196", - "menu": "\f197", - "mention": "\f198", - "loop": "\f199", - "logout": "\f19a", - "lock": "\f19b", - "lock-badge": "\f19c", - "location": "\f19d", - "link": "\f19e", - "link-broken": "\f19f", - "link-badge": "\f1a0", - "large-play": "\f1a1", - "large-pause": "\f1a2", - "language": "\f1a3", - "lamp": "\f1a4", - "keyboard": "\f1a5", - "key": "\f1a6", - "italic": "\f1a7", - "install": "\f1a8", - "info": "\f1a9", - "info-filled": "\f1aa", - "help": "\f1ab", - "heart": "\f1ac", - "heart-outline": "\f1ad", - "hd-photo": "\f1ae", - "hashtag": "\f1af", - "hand-stop": "\f1b0", - "grouped": "\f1b1", - "grouped-disable": "\f1b2", - "group": "\f1b3", - "group-filled": "\f1b4", - "gift": "\f1b5", - "gift-transfer-inline": "\f1b6", - "gifs": "\f1b7", - "fullscreen": "\f1b8", - "frozen-time": "\f1b9", - "fragment": "\f1ba", - "forward": "\f1bb", - "forums": "\f1bc", - "fontsize": "\f1bd", - "folder": "\f1be", - "folder-badge": "\f1bf", - "flag": "\f1c0", - "file-badge": "\f1c1", - "favorite": "\f1c2", - "favorite-filled": "\f1c3", - "eye": "\f1c4", - "eye-outline": "\f1c5", - "eye-crossed": "\f1c6", - "eye-crossed-outline": "\f1c7", - "expand": "\f1c8", - "expand-modal": "\f1c9", - "enter": "\f1ca", - "email": "\f1cb", - "edit": "\f1cc", - "eats": "\f1cd", - "dropdown-arrows": "\f1ce", - "download": "\f1cf", - "down": "\f1d0", - "double-badge": "\f1d1", - "document": "\f1d2", - "diamond": "\f1d3", - "delete": "\f1d4", - "delete-user": "\f1d5", - "delete-left": "\f1d6", - "delete-filled": "\f1d7", - "data": "\f1d8", - "darkmode": "\f1d9", - "crown-wear": "\f1da", - "crown-wear-outline": "\f1db", - "crown-take-off": "\f1dc", - "crown-take-off-outline": "\f1dd", - "craft": "\f1de", - "copy": "\f1df", - "copy-media": "\f1e0", - "comments": "\f1e1", - "comments-sticker": "\f1e2", - "combine-craft": "\f1e3", - "colorize": "\f1e4", - "collapse": "\f1e5", - "collapse-modal": "\f1e6", - "cloud-download": "\f1e7", - "closed-gift": "\f1e8", - "close": "\f1e9", - "close-topic": "\f1ea", - "close-circle": "\f1eb", - "clock": "\f1ec", - "clock-edit": "\f1ed", - "check": "\f1ee", - "chats-badge": "\f1ef", - "chat-badge": "\f1f0", - "channelviews": "\f1f1", - "channel": "\f1f2", - "channel-filled": "\f1f3", - "cash-circle": "\f1f4", - "card": "\f1f5", - "car": "\f1f6", - "camera": "\f1f7", - "camera-add": "\f1f8", - "calendar": "\f1f9", - "calendar-filter": "\f1fa", - "bug": "\f1fb", - "bots": "\f1fc", - "bot-commands-filled": "\f1fd", - "bot-command": "\f1fe", - "boosts": "\f1ff", - "boostcircle": "\f200", - "boost": "\f201", - "boost-outline": "\f202", - "boost-craft-chance": "\f203", - "bold": "\f204", - "avatar-saved-messages": "\f205", - "avatar-deleted-account": "\f206", - "avatar-archived-chats": "\f207", - "author-hidden": "\f208", - "auction": "\f209", - "auction-next-round": "\f20a", - "auction-filled": "\f20b", - "auction-drop": "\f20c", - "attach": "\f20d", - "ask-support": "\f20e", - "arrow-right": "\f20f", - "arrow-left": "\f210", - "arrow-down": "\f211", - "arrow-down-circle": "\f212", - "archive": "\f213", - "archive-to-main": "\f214", - "archive-from-main": "\f215", - "archive-filled": "\f216", - "animations": "\f217", - "animals": "\f218", - "allow-speak": "\f219", - "admin": "\f21a", - "add": "\f21b", - "add-user": "\f21c", - "add-user-filled": "\f21d", - "add-one-badge": "\f21e", - "add-filled": "\f21f", - "active-sessions": "\f220", - "rating-icons-negative": "\f221", - "rating-icons-level90": "\f222", - "rating-icons-level9": "\f223", - "rating-icons-level80": "\f224", - "rating-icons-level8": "\f225", - "rating-icons-level70": "\f226", - "rating-icons-level7": "\f227", - "rating-icons-level60": "\f228", - "rating-icons-level6": "\f229", - "rating-icons-level50": "\f22a", - "rating-icons-level5": "\f22b", - "rating-icons-level40": "\f22c", - "rating-icons-level4": "\f22d", - "rating-icons-level30": "\f22e", - "rating-icons-level3": "\f22f", - "rating-icons-level20": "\f230", - "rating-icons-level2": "\f231", - "rating-icons-level10": "\f232", - "rating-icons-level1": "\f233", - "folder-tabs-user": "\f234", - "folder-tabs-star": "\f235", - "folder-tabs-group": "\f236", - "folder-tabs-folder": "\f237", - "folder-tabs-chats": "\f238", - "folder-tabs-chat": "\f239", - "folder-tabs-channel": "\f23a", - "folder-tabs-bot": "\f23b", + "undo": "\f11c", + "understood": "\f11d", + "underlined": "\f11e", + "unarchive": "\f11f", + "truck": "\f120", + "transcribe": "\f121", + "trade": "\f122", + "topic-new": "\f123", + "tools": "\f124", + "toncoin": "\f125", + "timer": "\f126", + "tag": "\f127", + "tag-name": "\f128", + "tag-filter": "\f129", + "tag-crossed": "\f12a", + "tag-add": "\f12b", + "strikethrough": "\f12c", + "story-reply": "\f12d", + "story-priority": "\f12e", + "story-expired": "\f12f", + "story-caption": "\f130", + "stop": "\f131", + "stop-raising-hand": "\f132", + "stickers": "\f133", + "stealth-past": "\f134", + "stealth-future": "\f135", + "stats": "\f136", + "stars-refund": "\f137", + "stars-lock": "\f138", + "star": "\f139", + "sport": "\f13a", + "spoiler": "\f13b", + "spoiler-disable": "\f13c", + "speaker": "\f13d", + "speaker-story": "\f13e", + "speaker-outline": "\f13f", + "speaker-muted-story": "\f140", + "sort": "\f141", + "sort-by-price": "\f142", + "sort-by-number": "\f143", + "sort-by-date": "\f144", + "smile": "\f145", + "smallscreen": "\f146", + "skip-previous": "\f147", + "skip-next": "\f148", + "sidebar": "\f149", + "show-message": "\f14a", + "share-screen": "\f14b", + "share-screen-stop": "\f14c", + "share-screen-outlined": "\f14d", + "share-filled": "\f14e", + "settings": "\f14f", + "settings-filled": "\f150", + "send": "\f151", + "send-outline": "\f152", + "sell": "\f153", + "sell-outline": "\f154", + "select": "\f155", + "search": "\f156", + "sd-photo": "\f157", + "scheduled": "\f158", + "schedule": "\f159", + "saved-messages": "\f15a", + "save-story": "\f15b", + "rotate": "\f15c", + "revote": "\f15d", + "revenue-split": "\f15e", + "reply": "\f15f", + "reply-filled": "\f160", + "replies": "\f161", + "replace": "\f162", + "reorder-tabs": "\f163", + "reopen-topic": "\f164", + "remove": "\f165", + "remove-quote": "\f166", + "reload": "\f167", + "refund": "\f168", + "redo": "\f169", + "recent": "\f16a", + "readchats": "\f16b", + "radial-badge": "\f16c", + "quote": "\f16d", + "quote-text": "\f16e", + "proof-of-ownership": "\f16f", + "privacy-policy": "\f170", + "previous": "\f171", + "poll": "\f172", + "play": "\f173", + "play-story": "\f174", + "pip": "\f175", + "pinned-message": "\f176", + "pinned-chat": "\f177", + "pin": "\f178", + "pin-list": "\f179", + "pin-badge": "\f17a", + "photo": "\f17b", + "phone": "\f17c", + "phone-discard": "\f17d", + "phone-discard-outline": "\f17e", + "permissions": "\f17f", + "pause": "\f180", + "password-off": "\f181", + "open-in-new-tab": "\f182", + "one-filled": "\f183", + "note": "\f184", + "non-contacts": "\f185", + "noise-suppression": "\f186", + "nochannel": "\f187", + "next": "\f188", + "next-link": "\f189", + "new-chat-filled": "\f18a", + "my-notes": "\f18b", + "muted": "\f18c", + "mute": "\f18d", + "move-caption-up": "\f18e", + "move-caption-down": "\f18f", + "more": "\f190", + "more-circle": "\f191", + "monospace": "\f192", + "microphone": "\f193", + "microphone-alt": "\f194", + "message": "\f195", + "message-succeeded": "\f196", + "message-read": "\f197", + "message-pending": "\f198", + "message-failed": "\f199", + "menu": "\f19a", + "mention": "\f19b", + "loop": "\f19c", + "logout": "\f19d", + "lock": "\f19e", + "lock-badge": "\f19f", + "location": "\f1a0", + "link": "\f1a1", + "link-broken": "\f1a2", + "link-badge": "\f1a3", + "large-play": "\f1a4", + "large-pause": "\f1a5", + "language": "\f1a6", + "lamp": "\f1a7", + "keyboard": "\f1a8", + "key": "\f1a9", + "italic": "\f1aa", + "install": "\f1ab", + "info": "\f1ac", + "info-filled": "\f1ad", + "help": "\f1ae", + "heart": "\f1af", + "heart-outline": "\f1b0", + "hd-photo": "\f1b1", + "hashtag": "\f1b2", + "hand-stop": "\f1b3", + "grouped": "\f1b4", + "grouped-disable": "\f1b5", + "group": "\f1b6", + "group-filled": "\f1b7", + "gift": "\f1b8", + "gift-transfer-inline": "\f1b9", + "gifs": "\f1ba", + "fullscreen": "\f1bb", + "frozen-time": "\f1bc", + "fragment": "\f1bd", + "forward": "\f1be", + "forums": "\f1bf", + "fontsize": "\f1c0", + "folder": "\f1c1", + "folder-badge": "\f1c2", + "flip": "\f1c3", + "flag": "\f1c4", + "file-badge": "\f1c5", + "favorite": "\f1c6", + "favorite-filled": "\f1c7", + "eye": "\f1c8", + "eye-outline": "\f1c9", + "eye-crossed": "\f1ca", + "eye-crossed-outline": "\f1cb", + "expand": "\f1cc", + "expand-modal": "\f1cd", + "enter": "\f1ce", + "email": "\f1cf", + "edit": "\f1d0", + "eats": "\f1d1", + "dropdown-arrows": "\f1d2", + "download": "\f1d3", + "down": "\f1d4", + "double-badge": "\f1d5", + "document": "\f1d6", + "diamond": "\f1d7", + "delete": "\f1d8", + "delete-user": "\f1d9", + "delete-left": "\f1da", + "delete-filled": "\f1db", + "data": "\f1dc", + "darkmode": "\f1dd", + "crown-wear": "\f1de", + "crown-wear-outline": "\f1df", + "crown-take-off": "\f1e0", + "crown-take-off-outline": "\f1e1", + "crop": "\f1e2", + "craft": "\f1e3", + "copy": "\f1e4", + "copy-media": "\f1e5", + "comments": "\f1e6", + "comments-sticker": "\f1e7", + "combine-craft": "\f1e8", + "colorize": "\f1e9", + "collapse": "\f1ea", + "collapse-modal": "\f1eb", + "cloud-download": "\f1ec", + "closed-gift": "\f1ed", + "close": "\f1ee", + "close-topic": "\f1ef", + "close-circle": "\f1f0", + "clock": "\f1f1", + "clock-edit": "\f1f2", + "check": "\f1f3", + "chats-badge": "\f1f4", + "chat-badge": "\f1f5", + "channelviews": "\f1f6", + "channel": "\f1f7", + "channel-filled": "\f1f8", + "cash-circle": "\f1f9", + "card": "\f1fa", + "car": "\f1fb", + "camera": "\f1fc", + "camera-add": "\f1fd", + "calendar": "\f1fe", + "calendar-filter": "\f1ff", + "bug": "\f200", + "brush": "\f201", + "bots": "\f202", + "bot-commands-filled": "\f203", + "bot-command": "\f204", + "boosts": "\f205", + "boostcircle": "\f206", + "boost": "\f207", + "boost-outline": "\f208", + "boost-craft-chance": "\f209", + "bold": "\f20a", + "avatar-saved-messages": "\f20b", + "avatar-deleted-account": "\f20c", + "avatar-archived-chats": "\f20d", + "author-hidden": "\f20e", + "auction": "\f20f", + "auction-next-round": "\f210", + "auction-filled": "\f211", + "auction-drop": "\f212", + "attach": "\f213", + "ask-support": "\f214", + "arrow-right": "\f215", + "arrow-left": "\f216", + "arrow-down": "\f217", + "arrow-down-circle": "\f218", + "archive": "\f219", + "archive-to-main": "\f21a", + "archive-from-main": "\f21b", + "archive-filled": "\f21c", + "animations": "\f21d", + "animals": "\f21e", + "allow-speak": "\f21f", + "admin": "\f220", + "add": "\f221", + "add-user": "\f222", + "add-user-filled": "\f223", + "add-one-badge": "\f224", + "add-filled": "\f225", + "active-sessions": "\f226", + "folder-tabs-user": "\f227", + "folder-tabs-star": "\f228", + "folder-tabs-group": "\f229", + "folder-tabs-folder": "\f22a", + "folder-tabs-chats": "\f22b", + "folder-tabs-chat": "\f22c", + "folder-tabs-channel": "\f22d", + "folder-tabs-bot": "\f22e", + "rating-icons-negative": "\f22f", + "rating-icons-level90": "\f230", + "rating-icons-level9": "\f231", + "rating-icons-level80": "\f232", + "rating-icons-level8": "\f233", + "rating-icons-level70": "\f234", + "rating-icons-level7": "\f235", + "rating-icons-level60": "\f236", + "rating-icons-level6": "\f237", + "rating-icons-level50": "\f238", + "rating-icons-level5": "\f239", + "rating-icons-level40": "\f23a", + "rating-icons-level4": "\f23b", + "rating-icons-level30": "\f23c", + "rating-icons-level3": "\f23d", + "rating-icons-level20": "\f23e", + "rating-icons-level2": "\f23f", + "rating-icons-level10": "\f240", + "rating-icons-level1": "\f241", ); diff --git a/src/styles/icons.woff b/src/styles/icons.woff index ecabd7d7ca2588a6bdfea64f31318a2c0273b1df..7534a641d60452277c6c3e61ecaae7b31f309709 100644 GIT binary patch delta 40449 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%0AO`{)?01iD#Xw&O@LuqN|t<#@X0DhkCkU2QD2g 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 NI0(YR{{b04wCxg3VLAW+ delta 39812 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 AV*mgE diff --git a/src/styles/icons.woff2 b/src/styles/icons.woff2 index 8e9dee834e9664a4bfe2196b7a1a38766a80c302..1f50c0b468a60195a6ae00bb6d19e9637709ccb7 100644 GIT binary patch literal 33984 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 KyXy2ZbO`{Q;X8K# literal 33456 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Μtu6DOEKHXt}iA6MgbP8Q-J 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 diff --git a/src/types/icons/font.ts b/src/types/icons/font.ts index a98c8ec7a..fd6833b73 100644 --- a/src/types/icons/font.ts +++ b/src/types/icons/font.ts @@ -26,6 +26,7 @@ export type FontIconName = | 'unlist' | 'unlist-outline' | 'unique-profile' + | 'undo' | 'understood' | 'underlined' | 'unarchive' @@ -89,6 +90,7 @@ export type FontIconName = | 'schedule' | 'saved-messages' | 'save-story' + | 'rotate' | 'revote' | 'revenue-split' | 'reply' @@ -101,6 +103,7 @@ export type FontIconName = | 'remove-quote' | 'reload' | 'refund' + | 'redo' | 'recent' | 'readchats' | 'radial-badge' @@ -190,6 +193,7 @@ export type FontIconName = | 'fontsize' | 'folder' | 'folder-badge' + | 'flip' | 'flag' | 'file-badge' | 'favorite' @@ -220,6 +224,7 @@ export type FontIconName = | 'crown-wear-outline' | 'crown-take-off' | 'crown-take-off-outline' + | 'crop' | 'craft' | 'copy' | 'copy-media' @@ -250,6 +255,7 @@ export type FontIconName = | 'calendar' | 'calendar-filter' | 'bug' + | 'brush' | 'bots' | 'bot-commands-filled' | 'bot-command' @@ -287,6 +293,14 @@ export type FontIconName = | 'add-one-badge' | 'add-filled' | 'active-sessions' + | 'folder-tabs-user' + | 'folder-tabs-star' + | 'folder-tabs-group' + | 'folder-tabs-folder' + | 'folder-tabs-chats' + | 'folder-tabs-chat' + | 'folder-tabs-channel' + | 'folder-tabs-bot' | 'rating-icons-negative' | 'rating-icons-level90' | 'rating-icons-level9' @@ -305,12 +319,4 @@ export type FontIconName = | 'rating-icons-level20' | 'rating-icons-level2' | 'rating-icons-level10' - | 'rating-icons-level1' - | 'folder-tabs-user' - | 'folder-tabs-star' - | 'folder-tabs-group' - | 'folder-tabs-folder' - | 'folder-tabs-chats' - | 'folder-tabs-chat' - | 'folder-tabs-channel' - | 'folder-tabs-bot'; + | 'rating-icons-level1'; diff --git a/src/types/language.d.ts b/src/types/language.d.ts index a0ae21838..4f880dde8 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -1783,6 +1783,29 @@ export interface LangPair { 'GiftValueForSaleOnFragment': undefined; 'GiftValueForSaleOnTelegram': undefined; 'EmbeddedMessageNoCaption': undefined; + 'EditMedia': undefined; + 'Draw': undefined; + 'Crop': undefined; + 'Clear': undefined; + 'Undo': undefined; + 'Redo': undefined; + 'ResetCrop': undefined; + 'CustomColor': undefined; + 'Size': undefined; + 'Tool': undefined; + 'Pen': undefined; + 'Arrow': undefined; + 'Brush': undefined; + 'Neon': undefined; + 'Eraser': undefined; + 'AspectRatio': undefined; + 'Free': undefined; + 'Original': undefined; + 'Square': undefined; + 'HEX': undefined; + 'RGB': undefined; + 'Adjust': undefined; + 'Text': undefined; 'ConfirmBuyGiftForTonDescription': undefined; 'TitleGiftLocked': undefined; 'QuickPreview': undefined;