({ ...attachment, shouldSendAsFile: !attachment.voice ? true : undefined }))
+ ? attachments.map((attachment) => ({
+ ...attachment,
+ shouldSendAsFile: !attachment.voice ? true : undefined,
+ shouldSendAsSpoiler: undefined,
+ }))
: attachments;
}
diff --git a/src/components/middle/composer/hooks/useAttachmentModal.ts b/src/components/middle/composer/hooks/useAttachmentModal.ts
index fbeeb44d6..1430184e3 100644
--- a/src/components/middle/composer/hooks/useAttachmentModal.ts
+++ b/src/components/middle/composer/hooks/useAttachmentModal.ts
@@ -24,14 +24,14 @@ export default function useAttachmentModal({
setAttachments(MEMO_EMPTY_ARRAY);
}, [setAttachments]);
- const handleDeleteAttachment = useCallback((index: number) => {
- const newAttachments = attachments.filter((_, i) => i !== index);
- setAttachments(newAttachments?.length ? newAttachments : MEMO_EMPTY_ARRAY);
- }, [attachments, setAttachments]);
-
const handleSetAttachments = useCallback(
(newValue: ApiAttachment[] | ((current: ApiAttachment[]) => ApiAttachment[])) => {
const newAttachments = typeof newValue === 'function' ? newValue(attachments) : newValue;
+ if (!newAttachments.length) {
+ setAttachments(MEMO_EMPTY_ARRAY);
+ return;
+ }
+
if (newAttachments.some(({ size }) => size > fileSizeLimit)) {
openLimitReachedModal({
limit: 'uploadMaxFileparts',
@@ -42,10 +42,12 @@ export default function useAttachmentModal({
}, [attachments, fileSizeLimit, openLimitReachedModal, setAttachments],
);
- const handleAppendFiles = useCallback(async (files: File[]) => {
+ const handleAppendFiles = useCallback(async (files: File[], isSpoiler?: boolean) => {
handleSetAttachments([
...attachments,
- ...await Promise.all(files.map((file) => buildAttachment(file.name, file))),
+ ...await Promise.all(files.map((file) => (
+ buildAttachment(file.name, file, { shouldSendAsSpoiler: isSpoiler || undefined })
+ ))),
]);
}, [attachments, handleSetAttachments]);
@@ -60,7 +62,6 @@ export default function useAttachmentModal({
handleFileSelect,
onCaptionUpdate: setHtml,
handleClearAttachments,
- handleDeleteAttachment,
handleSetAttachments,
};
}
diff --git a/src/components/middle/message/InvoiceMediaPreview.module.scss b/src/components/middle/message/InvoiceMediaPreview.module.scss
index ac287b389..856fe85f6 100644
--- a/src/components/middle/message/InvoiceMediaPreview.module.scss
+++ b/src/components/middle/message/InvoiceMediaPreview.module.scss
@@ -6,62 +6,6 @@
cursor: pointer;
}
-.dots {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-
- --background-url: url('../../../assets/turbulence_1x.png');
- --background-size: 256px;
- background: rgba(0, 0, 0, 0.25) var(--background-url);
- background-size: var(--background-size) var(--background-size);
- z-index: 1;
-
- @media (-webkit-min-device-pixel-ratio: 2) {
- --background-url: url('../../../assets/turbulence_2x.png');
- }
-
- @media (-webkit-min-device-pixel-ratio: 3) {
- --background-url: url('../../../assets/turbulence_3x.png');
- }
-
- --x-direction: var(--background-size);
- --y-direction: 0;
- animation: 20s linear infinite dots;
-
- &::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-image: var(--background-url);
- background-size: var(--background-size) var(--background-size);
-
- --x-direction: 0;
- --y-direction: var(--background-size);
- animation: 20s linear -7s infinite dots;
- }
-
- &::after {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-image: var(--background-url);
- background-size: var(--background-size) var(--background-size);
-
- --x-direction: calc(-1 * var(--background-size));
- --y-direction: calc(-1 * var(--background-size));
- animation: 20s linear -14s infinite dots;
- }
-}
-
.duration {
z-index: 2;
position: absolute;
@@ -97,16 +41,6 @@
line-height: 1rem;
}
-.canvas {
- display: block;
- max-width: 100%;
-}
-
-@keyframes dots {
- 0% {
- background-position: 0 0;
- }
- 100% {
- background-position: var(--x-direction) var(--y-direction);
- }
+.spoiler {
+ position: static;
}
diff --git a/src/components/middle/message/InvoiceMediaPreview.tsx b/src/components/middle/message/InvoiceMediaPreview.tsx
index c406f7ac0..60ec3e554 100644
--- a/src/components/middle/message/InvoiceMediaPreview.tsx
+++ b/src/components/middle/message/InvoiceMediaPreview.tsx
@@ -10,9 +10,10 @@ import { formatMediaDuration } from '../../../util/dateFormat';
import buildClassName from '../../../util/buildClassName';
import useLang from '../../../hooks/useLang';
-import useCanvasBlur from '../../../hooks/useCanvasBlur';
import useInterval from '../../../hooks/useInterval';
+import MediaSpoiler from '../../common/MediaSpoiler';
+
import styles from './InvoiceMediaPreview.module.scss';
type OwnProps = {
@@ -21,7 +22,6 @@ type OwnProps = {
};
const POLLING_INTERVAL = 30000;
-const BLUR_RADIUS = 25;
const InvoiceMediaPreview: FC
= ({
message,
@@ -49,8 +49,6 @@ const InvoiceMediaPreview: FC = ({
width, height, thumbnail, duration,
} = extendedMedia!;
- const canvasRef = useCanvasBlur(thumbnail?.dataUri, false, undefined, BLUR_RADIUS, width, height);
-
const handleClick = useCallback(() => {
openInvoice({
chatId,
@@ -64,8 +62,13 @@ const InvoiceMediaPreview: FC = ({
className={buildClassName(styles.root, 'media-inner')}
onClick={handleClick}
>
-
-
+
{Boolean(duration) && {formatMediaDuration(duration)}
}
diff --git a/src/components/middle/message/Photo.tsx b/src/components/middle/message/Photo.tsx
index d3f23a821..96f8af21c 100644
--- a/src/components/middle/message/Photo.tsx
+++ b/src/components/middle/message/Photo.tsx
@@ -16,6 +16,7 @@ import {
getMediaTransferState,
isOwnMessage,
getMessageMediaFormat,
+ getMessageMediaThumbDataUri,
} from '../../../global/helpers';
import buildClassName from '../../../util/buildClassName';
import getCustomAppendixBg from './helpers/getCustomAppendixBg';
@@ -28,8 +29,10 @@ import useBlurredMediaThumbRef from './hooks/useBlurredMediaThumbRef';
import usePrevious from '../../../hooks/usePrevious';
import useMediaTransition from '../../../hooks/useMediaTransition';
import useLayoutEffectWithPrevDeps from '../../../hooks/useLayoutEffectWithPrevDeps';
+import useFlag from '../../../hooks/useFlag';
import ProgressSpinner from '../../ui/ProgressSpinner';
+import MediaSpoiler from '../../common/MediaSpoiler';
export type OwnProps = {
id?: string;
@@ -91,6 +94,9 @@ const Photo: FC = ({
const noThumb = Boolean(fullMediaData);
const thumbRef = useBlurredMediaThumbRef(message, noThumb);
const thumbClassNames = useMediaTransition(!noThumb);
+ const thumbDataUri = getMessageMediaThumbDataUri(message);
+
+ const [isSpoilerShown, , hideSpoiler] = useFlag(photo.isSpoiler);
const {
loadProgress: downloadProgress,
@@ -118,15 +124,22 @@ const Photo: FC = ({
const handleClick = useCallback(() => {
if (isUploading) {
- if (onCancelUpload) {
- onCancelUpload(message);
- }
- } else if (!fullMediaData) {
- setIsLoadAllowed((isAllowed) => !isAllowed);
- } else if (onClick) {
- onClick(message.id);
+ onCancelUpload?.(message);
+ return;
}
- }, [fullMediaData, isUploading, message, onCancelUpload, onClick]);
+
+ if (!fullMediaData) {
+ setIsLoadAllowed((isAllowed) => !isAllowed);
+ return;
+ }
+
+ if (isSpoilerShown) {
+ hideSpoiler();
+ return;
+ }
+
+ onClick?.(message.id);
+ }, [fullMediaData, hideSpoiler, isSpoilerShown, isUploading, message, onCancelUpload, onClick]);
const isOwn = isOwnMessage(message);
useLayoutEffectWithPrevDeps(([prevShouldAffectAppendix]) => {
@@ -184,6 +197,14 @@ const Photo: FC = ({
)}
{shouldRenderDownloadButton && }
+
{isTransferring && (
{Math.round(transferProgress * 100)}%
)}
diff --git a/src/components/middle/message/Video.tsx b/src/components/middle/message/Video.tsx
index 472514b00..abc09ec1d 100644
--- a/src/components/middle/message/Video.tsx
+++ b/src/components/middle/message/Video.tsx
@@ -30,6 +30,7 @@ import useFlag from '../../../hooks/useFlag';
import ProgressSpinner from '../../ui/ProgressSpinner';
import OptimizedVideo from '../../ui/OptimizedVideo';
+import MediaSpoiler from '../../common/MediaSpoiler';
export type OwnProps = {
id?: string;
@@ -74,6 +75,8 @@ const Video: FC = ({
const video = (getMessageVideo(message) || getMessageWebPageVideo(message))!;
const localBlobUrl = video.blobUrl;
+ const [isSpoilerShown, , hideSpoiler] = useFlag(video.isSpoiler);
+
const isIntersectingForLoading = useIsIntersecting(ref, observeIntersectionForLoading);
const isIntersectingForPlaying = (
useIsIntersecting(ref, observeIntersectionForPlaying)
@@ -86,7 +89,7 @@ const Video: FC = ({
const [isLoadAllowed, setIsLoadAllowed] = useState(canAutoLoad);
const shouldLoad = Boolean(isLoadAllowed && isIntersectingForLoading && lastSyncTime);
- const [isPlayAllowed, setIsPlayAllowed] = useState(canAutoPlay);
+ const [isPlayAllowed, setIsPlayAllowed] = useState(canAutoPlay && !isSpoilerShown);
const fullMediaHash = getMessageMediaHash(message, 'inline');
const [isFullMediaPreloaded] = useState(Boolean(fullMediaHash && mediaLoader.getFromMemory(fullMediaHash)));
@@ -96,7 +99,8 @@ const Video: FC = ({
const fullMediaData = localBlobUrl || mediaData;
const [isPlayerReady, markPlayerReady] = useFlag();
- const hasThumb = Boolean(getMessageMediaThumbDataUri(message));
+ const thumbDataUri = getMessageMediaThumbDataUri(message);
+ const hasThumb = Boolean(thumbDataUri);
const previewMediaHash = getMessageMediaHash(message, 'preview');
const [isPreviewPreloaded] = useState(Boolean(previewMediaHash && mediaLoader.getFromMemory(previewMediaHash)));
@@ -147,19 +151,34 @@ const Video: FC = ({
const handleClick = useCallback(() => {
if (isUploading) {
- if (onCancelUpload) {
- onCancelUpload(message);
- }
- } else if (isDownloading) {
- getActions().cancelMessageMediaDownload({ message });
- } else if (!fullMediaData) {
- setIsLoadAllowed((isAllowed) => !isAllowed);
- } else if (fullMediaData && !isPlayAllowed) {
- setIsPlayAllowed(true);
- } else if (onClick) {
- onClick(message.id);
+ onCancelUpload?.(message);
+ return;
}
- }, [isUploading, isDownloading, fullMediaData, isPlayAllowed, onClick, onCancelUpload, message]);
+
+ if (isDownloading) {
+ getActions().cancelMessageMediaDownload({ message });
+ return;
+ }
+
+ if (!fullMediaData) {
+ setIsLoadAllowed((isAllowed) => !isAllowed);
+ return;
+ }
+
+ if (fullMediaData && !isPlayAllowed) {
+ setIsPlayAllowed(true);
+ }
+
+ if (isSpoilerShown) {
+ hideSpoiler();
+ return;
+ }
+
+ onClick?.(message.id);
+ }, [
+ isUploading, isDownloading, fullMediaData, isPlayAllowed, isSpoilerShown, onClick, message, onCancelUpload,
+ hideSpoiler,
+ ]);
const className = buildClassName('media-inner dark', !isUploading && 'interactive');
@@ -202,6 +221,14 @@ const Video: FC = ({
)}
{isProtected && }
+
{shouldRenderSpinner && (
diff --git a/src/global/helpers/messageMedia.ts b/src/global/helpers/messageMedia.ts
index 669c4c6b6..2a7ee2afd 100644
--- a/src/global/helpers/messageMedia.ts
+++ b/src/global/helpers/messageMedia.ts
@@ -134,7 +134,8 @@ export function getMessageMediaThumbnail(message: ApiMessage) {
|| getMessageDocument(message)
|| getMessageSticker(message)
|| getMessageWebPagePhoto(message)
- || getMessageWebPageVideo(message);
+ || getMessageWebPageVideo(message)
+ || getMessageInvoice(message)?.extendedMedia;
if (!media) {
return undefined;
@@ -147,6 +148,14 @@ export function getMessageMediaThumbDataUri(message: ApiMessage) {
return getMessageMediaThumbnail(message)?.dataUri;
}
+export function getMessageIsSpoiler(message: ApiMessage) {
+ const media = getMessagePhoto(message)
+ || getMessageVideo(message);
+
+ const invoiceMedia = getMessageInvoice(message)?.extendedMedia;
+ return Boolean(invoiceMedia || media?.isSpoiler);
+}
+
export function getDocumentMediaHash(document: ApiDocument) {
return `document${document.id}`;
}
diff --git a/src/hooks/useCanvasBlur.ts b/src/hooks/useCanvasBlur.ts
index 932c25553..5c0497e59 100644
--- a/src/hooks/useCanvasBlur.ts
+++ b/src/hooks/useCanvasBlur.ts
@@ -2,6 +2,7 @@ import { useEffect, useRef } from '../lib/teact/teact';
import { IS_CANVAS_FILTER_SUPPORTED } from '../util/environment';
import fastBlur from '../lib/fastBlur';
+import useOnChange from './useOnChange';
const RADIUS = 2;
const ITERATIONS = 2;
@@ -18,6 +19,12 @@ export default function useCanvasBlur(
const canvasRef = useRef
(null);
const isStarted = useRef();
+ useOnChange(() => {
+ if (!isDisabled) {
+ isStarted.current = false;
+ }
+ }, [dataUri, isDisabled]);
+
useEffect(() => {
const canvas = canvasRef.current;
diff --git a/src/hooks/useScrolledState.ts b/src/hooks/useScrolledState.ts
new file mode 100644
index 000000000..1746e3c99
--- /dev/null
+++ b/src/hooks/useScrolledState.ts
@@ -0,0 +1,17 @@
+import { useCallback, useState } from '../lib/teact/teact';
+
+const THRESHOLD = 5;
+
+export default function useScrolledState(threshold = THRESHOLD) {
+ const [isAtBeginning, setIsAtBeginning] = useState(true);
+ const [isAtEnd, setIsAtEnd] = useState(true);
+
+ const handleScroll = useCallback((e: React.UIEvent) => {
+ const { scrollHeight, scrollTop, clientHeight } = e.target as HTMLElement;
+
+ setIsAtBeginning(scrollTop < threshold);
+ setIsAtEnd(scrollHeight - scrollTop - clientHeight < threshold);
+ }, [threshold]);
+
+ return { isAtBeginning, isAtEnd, handleScroll };
+}
diff --git a/src/hooks/useShowTransition.ts b/src/hooks/useShowTransition.ts
index b890bcba0..c5f0bb5da 100644
--- a/src/hooks/useShowTransition.ts
+++ b/src/hooks/useShowTransition.ts
@@ -9,6 +9,7 @@ const useShowTransition = (
noOpenTransition = false,
className: string | false = 'fast',
noCloseTransition = false,
+ closeDuration = CLOSE_DURATION,
) => {
const [isClosed, setIsClosed] = useState(!isOpen);
const closeTimeoutRef = useRef();
@@ -40,7 +41,7 @@ const useShowTransition = (
if (noCloseTransition) {
exec();
} else {
- closeTimeoutRef.current = window.setTimeout(exec, CLOSE_DURATION);
+ closeTimeoutRef.current = window.setTimeout(exec, closeDuration);
}
}
}
diff --git a/src/styles/Telegram T.json b/src/styles/Telegram T.json
index 860d88df2..54afe7f3a 100644
--- a/src/styles/Telegram T.json
+++ b/src/styles/Telegram T.json
@@ -2068,21 +2068,19 @@
{
"id": 101,
"paths": [
- "M660.48 128c0 25.6-20.48 40.96-40.96 40.96-25.6 0-40.96-20.48-40.96-40.96 0-25.6 20.48-40.96 40.96-40.96s40.96 15.36 40.96 40.96zM619.52 363.52c30.72 0 51.2-25.6 51.2-51.2 0-30.72-25.6-51.2-51.2-51.2-30.72 0-51.2 25.6-51.2 51.2-5.12 25.6 20.48 51.2 51.2 51.2zM788.48 563.2c30.72 0 51.2-25.6 51.2-51.2 0-30.72-25.6-51.2-51.2-51.2-30.72 0-51.2 25.6-51.2 51.2 0 30.72 20.48 51.2 51.2 51.2zM983.040 512c0 25.6-20.48 40.96-40.96 40.96-25.6 0-40.96-20.48-40.96-40.96 0-25.6 20.48-40.96 40.96-40.96s40.96 15.36 40.96 40.96zM619.52 936.96c25.6 0 40.96-20.48 40.96-40.96s-20.48-40.96-40.96-40.96c-25.6 0-40.96 20.48-40.96 40.96s15.36 40.96 40.96 40.96zM788.48 353.28c25.6 0 40.96-20.48 40.96-40.96 0-25.6-20.48-40.96-40.96-40.96-25.6 0-40.96 20.48-40.96 40.96s20.48 40.96 40.96 40.96zM819.2 128c0 15.36-15.36 30.72-30.72 30.72s-30.72-15.36-30.72-30.72c0-15.36 15.36-30.72 30.72-30.72 20.48 0 30.72 15.36 30.72 30.72zM936.96 343.040c20.48 0 35.84-15.36 35.84-35.84s-15.36-30.72-30.72-30.72c-15.36 0-30.72 15.36-30.72 30.72s10.24 35.84 25.6 35.84zM972.8 727.040c0 15.36-15.36 30.72-30.72 30.72s-30.72-15.36-30.72-30.72c0-15.36 15.36-30.72 30.72-30.72 15.36-5.12 30.72 10.24 30.72 30.72zM936.96 148.48c10.24 0 20.48-10.24 20.48-20.48s-10.24-20.48-20.48-20.48c-10.24 0-20.48 10.24-20.48 20.48s10.24 20.48 20.48 20.48zM399.36 87.040v0 0c-46.080 0-87.040 0-117.76 0-25.6 5.12-56.32 10.24-81.92 20.48-30.72 15.36-56.32 40.96-76.8 71.68l61.44 61.44c0 0 0 0 0-5.12 10.24-25.6 30.72-46.080 56.32-56.32 10.24-5.12 25.6-10.24 51.2-10.24s61.44 0 107.52 0h66.56c25.6-0 46.080-15.36 46.080-40.96s-20.48-40.96-40.96-40.96h-71.68zM174.080 291.84c0 25.6 0 61.44 0 107.52v220.16c0 46.080 0 81.92 0 107.52 0 5.12 0 5.12 0 5.12l66.56-107.52c15.36-20.48 25.6-40.96 35.84-56.32s25.6-30.72 46.080-35.84c25.6-10.24 56.32-10.24 87.040 0 20.48 10.24 35.84 25.6 46.080 35.84 10.24 15.36 20.48 35.84 35.84 56.32v0l5.12 5.12c10.24 20.48 5.12 46.080-15.36 56.32s-46.080 5.12-56.32-15.36l-5.12-5.12c-15.36-25.6-25.6-40.96-30.72-51.2-15.36 5.12-15.36 0-20.48 0s-10.24 0-15.36 0c0 0-5.12 0-10.24 10.24-10.24 10.24-15.36 25.6-30.72 51.2l-92.16 148.48c5.12 5.12 15.36 10.24 20.48 15.36 10.24 5.12 25.6 10.24 51.2 10.24s61.44 0 107.52 0h66.56c25.6 0 40.96 20.48 40.96 40.96 0 25.6-20.48 40.96-40.96 40.96h-66.56c-46.080 0-81.92 0-112.64 0s-56.32-10.24-81.92-20.48c-40.96-20.48-71.68-51.2-92.16-92.16-15.36-25.6-20.48-51.2-20.48-81.92s0-66.56 0-112.64v0-225.28c0-46.080 0-81.92 0-112.64 0-25.6 5.12-46.080 10.24-66.56l71.68 71.68z",
- "M128 128l768 768z",
- "M896 936.96c-10.24 0-20.48-5.12-30.72-10.24l-768-768c-15.36-15.36-15.36-46.080 0-61.44s46.080-15.36 61.44 0l768 768c15.36 15.36 15.36 46.080 0 61.44-10.24 10.24-20.48 10.24-30.72 10.24z"
+ "M465.92 849.92h-174.080c-25.6 0-40.96-5.12-51.2-10.24-5.12-5.12-15.36-10.24-20.48-15.36l92.16-148.48c15.36-25.6 20.48-40.96 30.72-51.2 5.12-10.24 10.24-10.24 10.24-10.24h15.36c5.12 0 5.12 5.12 20.48 0 5.12 10.24 15.36 25.6 30.72 51.2l5.12 5.12c10.24 20.48 35.84 25.6 56.32 15.36s25.6-35.84 15.36-56.32l-5.12-5.12c-15.36-20.48-25.6-40.96-35.84-56.32-10.24-10.24-25.6-25.6-46.080-35.84-30.72-10.24-61.44-10.24-87.040 0-20.48 5.12-35.84 20.48-46.080 35.84s-20.48 35.84-35.84 56.32l-66.56 107.52c0 0 0 0 0-5.12v-435.2l-71.68-71.68c-5.12 20.48-10.24 40.96-10.24 66.56v450.56c0 30.72 5.12 56.32 20.48 81.92 20.48 40.96 51.2 71.68 92.16 92.16 25.6 10.24 51.2 20.48 81.92 20.48h179.2c20.48 0 40.96-15.36 40.96-40.96 0-20.48-15.36-40.96-40.96-40.96zM936.96 107.52c-10.24 0-20.48 10.24-20.48 20.48s10.24 20.48 20.48 20.48 20.48-10.24 20.48-20.48-10.24-20.48-20.48-20.48zM942.080 696.32c-15.36 0-30.72 15.36-30.72 30.72s15.36 30.72 30.72 30.72c15.36 0 30.72-15.36 30.72-30.72 0-20.48-15.36-35.84-30.72-30.72zM942.080 276.48c-15.36 0-30.72 15.36-30.72 30.72s10.24 35.84 25.6 35.84c20.48 0 35.84-15.36 35.84-35.84s-15.36-30.72-30.72-30.72zM788.48 97.28c-15.36 0-30.72 15.36-30.72 30.72s15.36 30.72 30.72 30.72c15.36 0 30.72-15.36 30.72-30.72s-10.24-30.72-30.72-30.72zM788.48 271.36c-25.6 0-40.96 20.48-40.96 40.96s20.48 40.96 40.96 40.96c25.6 0 40.96-20.48 40.96-40.96 0-25.6-20.48-40.96-40.96-40.96zM619.52 855.040c-25.6 0-40.96 20.48-40.96 40.96s15.36 40.96 40.96 40.96 40.96-20.48 40.96-40.96-20.48-40.96-40.96-40.96zM942.080 471.040c-20.48 0-40.96 15.36-40.96 40.96 0 20.48 15.36 40.96 40.96 40.96 20.48 0 40.96-15.36 40.96-40.96s-20.48-40.96-40.96-40.96zM788.48 460.8c-30.72 0-51.2 25.6-51.2 51.2 0 30.72 20.48 51.2 51.2 51.2s51.2-25.6 51.2-51.2c0-30.72-25.6-51.2-51.2-51.2zM619.52 261.12c-30.72 0-51.2 25.6-51.2 51.2-5.12 25.6 20.48 51.2 51.2 51.2s51.2-25.6 51.2-51.2c0-30.72-25.6-51.2-51.2-51.2zM619.52 87.040c-20.48 0-40.96 15.36-40.96 40.96 0 20.48 15.36 40.96 40.96 40.96 20.48 0 40.96-15.36 40.96-40.96s-20.48-40.96-40.96-40.96z",
+ "M926.72 926.72c-10.24 10.24-20.48 10.24-30.72 10.24s-20.48-5.12-30.72-10.24l-768-768c-15.36-15.36-15.36-46.080 0-61.44s46.080-15.36 61.44 0l20.48 20.48c5.12-5.12 10.24-10.24 20.48-10.24 25.6-10.24 56.32-15.36 81.92-20.48h189.44c20.48 0 40.96 15.36 40.96 40.96s-20.48 40.96-46.080 40.96h-174.080c-25.6 0-40.96 5.12-51.2 10.24l686.080 686.080c15.36 15.36 15.36 46.080 0 61.44z"
],
"attrs": [
- {},
{},
{}
],
- "isMulticolor": false,
- "isMulticolor2": false,
"grid": 24,
"tags": [
"spoiler-disable"
- ]
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false
},
{
"id": 100,
@@ -4526,4 +4524,4 @@
"showLiga": false
},
"uid": -1
-}
\ No newline at end of file
+}