GIF: Support sending with caption (#6755)
This commit is contained in:
parent
07d24e2d43
commit
b5fed8276a
@ -662,6 +662,10 @@ function buildReplyInfo(inputInfo: ApiInputReplyInfo, isForum?: boolean): ApiRep
|
|||||||
export function buildUploadingMedia(
|
export function buildUploadingMedia(
|
||||||
attachment: ApiAttachment,
|
attachment: ApiAttachment,
|
||||||
): MediaContent {
|
): MediaContent {
|
||||||
|
if (attachment.gif) {
|
||||||
|
return { video: attachment.gif };
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
filename: fileName,
|
filename: fileName,
|
||||||
blobUrl,
|
blobUrl,
|
||||||
|
|||||||
@ -197,14 +197,14 @@ export function buildInputDocument(media: ApiSticker | ApiVideo) {
|
|||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildInputMediaDocument(media: ApiSticker | ApiVideo) {
|
export function buildInputMediaDocument(media: ApiSticker | ApiVideo, spoiler?: true) {
|
||||||
const inputDocument = buildInputDocument(media);
|
const inputDocument = buildInputDocument(media);
|
||||||
|
|
||||||
if (!inputDocument) {
|
if (!inputDocument) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GramJs.InputMediaDocument({ id: inputDocument });
|
return new GramJs.InputMediaDocument({ id: inputDocument, spoiler });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildInputPoll(pollParams: ApiNewPoll, randomId: bigint) {
|
export function buildInputPoll(pollParams: ApiNewPoll, randomId: bigint) {
|
||||||
|
|||||||
@ -438,6 +438,9 @@ export function sendApiMessage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!media && attachment?.gif) {
|
||||||
|
media = buildInputMediaDocument(attachment.gif, attachment.shouldSendAsSpoiler);
|
||||||
|
}
|
||||||
if (!media && attachment) {
|
if (!media && attachment) {
|
||||||
try {
|
try {
|
||||||
media = await uploadMedia(localMessage, attachment, onProgress!);
|
media = await uploadMedia(localMessage, attachment, onProgress!);
|
||||||
@ -607,27 +610,33 @@ function sendGroupedMedia(
|
|||||||
|
|
||||||
const prevMediaQueue = mediaQueue;
|
const prevMediaQueue = mediaQueue;
|
||||||
mediaQueue = (async () => {
|
mediaQueue = (async () => {
|
||||||
let media;
|
let inputMedia: GramJs.TypeInputMedia | undefined;
|
||||||
try {
|
|
||||||
media = await uploadMedia(localMessage, attachment, onProgress!);
|
if (attachment.gif) {
|
||||||
} catch (err) {
|
inputMedia = buildInputMediaDocument(attachment.gif, attachment.shouldSendAsSpoiler);
|
||||||
if (DEBUG) {
|
} else {
|
||||||
// eslint-disable-next-line no-console
|
let media;
|
||||||
console.warn(err);
|
try {
|
||||||
|
media = await uploadMedia(localMessage, attachment, onProgress!);
|
||||||
|
} catch (err) {
|
||||||
|
if (DEBUG) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
groupedUploads[groupedId].counter--;
|
||||||
|
|
||||||
|
await prevMediaQueue;
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
groupedUploads[groupedId].counter--;
|
inputMedia = await fetchInputMedia(
|
||||||
|
buildInputPeer(chat.id, chat.accessHash),
|
||||||
await prevMediaQueue;
|
media,
|
||||||
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputMedia = await fetchInputMedia(
|
|
||||||
buildInputPeer(chat.id, chat.accessHash),
|
|
||||||
media,
|
|
||||||
);
|
|
||||||
|
|
||||||
await prevMediaQueue;
|
await prevMediaQueue;
|
||||||
|
|
||||||
if (!inputMedia) {
|
if (!inputMedia) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { CallbackAction } from '../../global/types';
|
import type { CallbackAction } from '../../global/types';
|
||||||
import type { IconName } from '../../types/icons';
|
import type { IconName } from '../../types/icons';
|
||||||
import type { LangFnParameters, RegularLangFnParameters } from '../../util/localization';
|
import type { LangFnParameters, RegularLangFnParameters } from '../../util/localization';
|
||||||
import type { ApiDocument, ApiFormattedText, ApiMessageEntity, ApiPhoto, ApiReaction } from './messages';
|
import type { ApiDocument, ApiFormattedText, ApiMessageEntity, ApiPhoto, ApiReaction, ApiVideo } from './messages';
|
||||||
import type { ApiPremiumSection } from './payments';
|
import type { ApiPremiumSection } from './payments';
|
||||||
import type { ApiBotVerification } from './peers';
|
import type { ApiBotVerification } from './peers';
|
||||||
import type { ApiStarsSubscriptionPricing } from './stars';
|
import type { ApiStarsSubscriptionPricing } from './stars';
|
||||||
@ -44,7 +44,7 @@ export interface ApiOnProgress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiAttachment {
|
export interface ApiAttachment {
|
||||||
blob: Blob;
|
blob?: Blob;
|
||||||
blobUrl: string;
|
blobUrl: string;
|
||||||
compressedBlobUrl?: string;
|
compressedBlobUrl?: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
@ -72,6 +72,8 @@ export interface ApiAttachment {
|
|||||||
uniqueId?: string;
|
uniqueId?: string;
|
||||||
ttlSeconds?: number;
|
ttlSeconds?: number;
|
||||||
shouldSendInHighQuality?: boolean;
|
shouldSendInHighQuality?: boolean;
|
||||||
|
|
||||||
|
gif?: ApiVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiWallpaper {
|
export interface ApiWallpaper {
|
||||||
|
|||||||
1
src/assets/font-icons/add-caption.svg
Normal file
1
src/assets/font-icons/add-caption.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="a" viewBox="0 0 32 32"><path d="M24.241 8.014H3.18c-.741 0-1.342-.604-1.342-1.35 0-.745.601-1.349 1.342-1.349H24.24c.74 0 1.342.604 1.342 1.35 0 .745-.601 1.35-1.342 1.35" /><rect width="13.437" height="2.699" x="1.837" y="22.019" rx="1.35" ry="1.35"/><rect width="19.521" height="2.699" x="1.837" y="13.667" rx="1.35" ry="1.35"/><rect width="2.699" height="11.86" x="22.883" y="17.439" rx="1.35" ry="1.35"/><rect width="11.86" height="2.699" x="18.302" y="22.019" rx="1.35" ry="1.35"/></svg>
|
||||||
|
After Width: | Height: | Size: 540 B |
@ -2666,6 +2666,8 @@
|
|||||||
"AttachmentSendAudio_other" = "Send {count} Audios";
|
"AttachmentSendAudio_other" = "Send {count} Audios";
|
||||||
"AttachmentSendFile_one" = "Send File";
|
"AttachmentSendFile_one" = "Send File";
|
||||||
"AttachmentSendFile_other" = "Send {count} Files";
|
"AttachmentSendFile_other" = "Send {count} Files";
|
||||||
|
"AttachmentSendGif" = "Send GIF";
|
||||||
|
"AttachmentReplaceGif" = "Replace GIF";
|
||||||
"AttachmentDragAddItems" = "Add Items";
|
"AttachmentDragAddItems" = "Add Items";
|
||||||
"AttachmentCaptionPlaceholder" = "Add a caption...";
|
"AttachmentCaptionPlaceholder" = "Add a caption...";
|
||||||
"MessageSummaryTitle" = "AI Summary";
|
"MessageSummaryTitle" = "AI Summary";
|
||||||
@ -2729,3 +2731,4 @@
|
|||||||
"GiftPreviewToggleRegularModels" = "View Primary Models >";
|
"GiftPreviewToggleRegularModels" = "View Primary Models >";
|
||||||
"AriaGiftPreviewPlay" = "Play random previews";
|
"AriaGiftPreviewPlay" = "Play random previews";
|
||||||
"AriaGiftPreviewStop" = "Pause random previews";
|
"AriaGiftPreviewStop" = "Pause random previews";
|
||||||
|
"MenuAddCaption" = "Add Caption";
|
||||||
|
|||||||
@ -132,7 +132,10 @@ import { getServerTime } from '../../util/serverTime';
|
|||||||
import windowSize from '../../util/windowSize';
|
import windowSize from '../../util/windowSize';
|
||||||
import { DEFAULT_MAX_MESSAGE_LENGTH } from '../../limits';
|
import { DEFAULT_MAX_MESSAGE_LENGTH } from '../../limits';
|
||||||
import applyIosAutoCapitalizationFix from '../middle/composer/helpers/applyIosAutoCapitalizationFix';
|
import applyIosAutoCapitalizationFix from '../middle/composer/helpers/applyIosAutoCapitalizationFix';
|
||||||
import buildAttachment, { prepareAttachmentsToSend } from '../middle/composer/helpers/buildAttachment';
|
import buildAttachment, {
|
||||||
|
buildGifAttachment,
|
||||||
|
prepareAttachmentsToSend,
|
||||||
|
} from '../middle/composer/helpers/buildAttachment';
|
||||||
import { buildCustomEmojiHtml } from '../middle/composer/helpers/customEmoji';
|
import { buildCustomEmojiHtml } from '../middle/composer/helpers/customEmoji';
|
||||||
import { isSelectionInsideInput } from '../middle/composer/helpers/selection';
|
import { isSelectionInsideInput } from '../middle/composer/helpers/selection';
|
||||||
import renderText from './helpers/renderText';
|
import renderText from './helpers/renderText';
|
||||||
@ -1021,6 +1024,8 @@ const Composer = ({
|
|||||||
theme,
|
theme,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const hasGifFromPicker = attachments.some((a) => a.gif);
|
||||||
|
|
||||||
useClipboardPaste(
|
useClipboardPaste(
|
||||||
isForCurrentMessageList || isInStoryViewer,
|
isForCurrentMessageList || isInStoryViewer,
|
||||||
insertFormattedTextAndUpdateCursor,
|
insertFormattedTextAndUpdateCursor,
|
||||||
@ -1030,6 +1035,7 @@ const Composer = ({
|
|||||||
!isCurrentUserPremium && !isChatWithSelf,
|
!isCurrentUserPremium && !isChatWithSelf,
|
||||||
showCustomEmojiPremiumNotification,
|
showCustomEmojiPremiumNotification,
|
||||||
!attachments.length,
|
!attachments.length,
|
||||||
|
hasGifFromPicker,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleEmbeddedClear = useLastCallback(() => {
|
const handleEmbeddedClear = useLastCallback(() => {
|
||||||
@ -1474,6 +1480,11 @@ const Composer = ({
|
|||||||
clearDraft({ chatId, threadId, isLocalOnly: true });
|
clearDraft({ chatId, threadId, isLocalOnly: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleGifAddCaption = useLastCallback((gif: ApiVideo) => {
|
||||||
|
handleSetAttachments([buildGifAttachment(gif)]);
|
||||||
|
closeSymbolMenu();
|
||||||
|
});
|
||||||
|
|
||||||
const handleStickerSelect = useLastCallback((
|
const handleStickerSelect = useLastCallback((
|
||||||
sticker: ApiSticker,
|
sticker: ApiSticker,
|
||||||
isSilent?: boolean,
|
isSilent?: boolean,
|
||||||
@ -2234,6 +2245,7 @@ const Composer = ({
|
|||||||
canSendGifs={canSendGifs}
|
canSendGifs={canSendGifs}
|
||||||
isMessageComposer={isInMessageList}
|
isMessageComposer={isInMessageList}
|
||||||
onGifSelect={handleGifSelect}
|
onGifSelect={handleGifSelect}
|
||||||
|
onGifAddCaption={handleGifAddCaption}
|
||||||
onStickerSelect={handleStickerSelect}
|
onStickerSelect={handleStickerSelect}
|
||||||
onCustomEmojiSelect={handleCustomEmojiSelect}
|
onCustomEmojiSelect={handleCustomEmojiSelect}
|
||||||
onRemoveSymbol={removeSymbol}
|
onRemoveSymbol={removeSymbol}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import useBuffering from '../../hooks/useBuffering';
|
|||||||
import useCanvasBlur from '../../hooks/useCanvasBlur';
|
import useCanvasBlur from '../../hooks/useCanvasBlur';
|
||||||
import useContextMenuHandlers from '../../hooks/useContextMenuHandlers';
|
import useContextMenuHandlers from '../../hooks/useContextMenuHandlers';
|
||||||
import { useIsIntersecting } from '../../hooks/useIntersectionObserver';
|
import { useIsIntersecting } from '../../hooks/useIntersectionObserver';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import useMedia from '../../hooks/useMedia';
|
import useMedia from '../../hooks/useMedia';
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
@ -33,9 +34,10 @@ type OwnProps = {
|
|||||||
observeIntersection: ObserveFn;
|
observeIntersection: ObserveFn;
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
isSavedMessages?: boolean;
|
||||||
onClick?: (gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
onClick?: (gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||||
onUnsaveClick?: (gif: ApiVideo) => void;
|
onUnsaveClick?: (gif: ApiVideo) => void;
|
||||||
isSavedMessages?: boolean;
|
onAddCaption?: (gif: ApiVideo) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GifButton: FC<OwnProps> = ({
|
const GifButton: FC<OwnProps> = ({
|
||||||
@ -43,13 +45,15 @@ const GifButton: FC<OwnProps> = ({
|
|||||||
isDisabled,
|
isDisabled,
|
||||||
className,
|
className,
|
||||||
observeIntersection,
|
observeIntersection,
|
||||||
|
isSavedMessages,
|
||||||
onClick,
|
onClick,
|
||||||
onUnsaveClick,
|
onUnsaveClick,
|
||||||
isSavedMessages,
|
onAddCaption,
|
||||||
}) => {
|
}) => {
|
||||||
const ref = useRef<HTMLDivElement>();
|
const ref = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
const lang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
||||||
const loadAndPlay = isIntersecting && !isDisabled;
|
const loadAndPlay = isIntersecting && !isDisabled;
|
||||||
@ -76,6 +80,7 @@ const GifButton: FC<OwnProps> = ({
|
|||||||
const getTriggerElement = useLastCallback(() => ref.current);
|
const getTriggerElement = useLastCallback(() => ref.current);
|
||||||
const getRootElement = useLastCallback(() => ref.current!.closest('.custom-scroll, .no-scrollbar'));
|
const getRootElement = useLastCallback(() => ref.current!.closest('.custom-scroll, .no-scrollbar'));
|
||||||
const getMenuElement = useLastCallback(() => ref.current!.querySelector('.gif-context-menu .bubble'));
|
const getMenuElement = useLastCallback(() => ref.current!.querySelector('.gif-context-menu .bubble'));
|
||||||
|
const getLayout = useLastCallback(() => ({ shouldAvoidNegativePosition: true }));
|
||||||
|
|
||||||
const handleClick = useLastCallback(() => {
|
const handleClick = useLastCallback(() => {
|
||||||
if (isContextMenuOpen || !onClick) return;
|
if (isContextMenuOpen || !onClick) return;
|
||||||
@ -109,6 +114,13 @@ const GifButton: FC<OwnProps> = ({
|
|||||||
}, undefined, true);
|
}, undefined, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleAddCaption = useLastCallback(() => {
|
||||||
|
onAddCaption?.({
|
||||||
|
...gif,
|
||||||
|
blobUrl: videoData,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const handleMouseDown = useLastCallback((e: React.MouseEvent<HTMLElement>) => {
|
const handleMouseDown = useLastCallback((e: React.MouseEvent<HTMLElement>) => {
|
||||||
preventMessageInputBlurWithBubbling(e);
|
preventMessageInputBlurWithBubbling(e);
|
||||||
handleBeforeContextMenu(e);
|
handleBeforeContextMenu(e);
|
||||||
@ -182,17 +194,21 @@ const GifButton: FC<OwnProps> = ({
|
|||||||
getTriggerElement={getTriggerElement}
|
getTriggerElement={getTriggerElement}
|
||||||
getRootElement={getRootElement}
|
getRootElement={getRootElement}
|
||||||
getMenuElement={getMenuElement}
|
getMenuElement={getMenuElement}
|
||||||
|
getLayout={getLayout}
|
||||||
className="gif-context-menu"
|
className="gif-context-menu"
|
||||||
autoClose
|
autoClose
|
||||||
onClose={handleContextMenuClose}
|
onClose={handleContextMenuClose}
|
||||||
onCloseAnimationEnd={handleContextMenuHide}
|
onCloseAnimationEnd={handleContextMenuHide}
|
||||||
>
|
>
|
||||||
{!isSavedMessages && <MenuItem onClick={handleSendQuiet} icon="mute">{lang('SendWithoutSound')}</MenuItem>}
|
{!isSavedMessages && <MenuItem onClick={handleSendQuiet} icon="mute">{oldLang('SendWithoutSound')}</MenuItem>}
|
||||||
<MenuItem onClick={handleSendScheduled} icon="calendar">
|
<MenuItem onClick={handleSendScheduled} icon="calendar">
|
||||||
{lang(isSavedMessages ? 'SetReminder' : 'ScheduleMessage')}
|
{oldLang(isSavedMessages ? 'SetReminder' : 'ScheduleMessage')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
{onAddCaption && (
|
||||||
|
<MenuItem icon="add-caption" onClick={handleAddCaption}>{lang('MenuAddCaption')}</MenuItem>
|
||||||
|
)}
|
||||||
{onUnsaveClick && (
|
{onUnsaveClick && (
|
||||||
<MenuItem destructive icon="delete" onClick={handleContextDelete}>{lang('Delete')}</MenuItem>
|
<MenuItem destructive icon="delete" onClick={handleContextDelete}>{oldLang('Delete')}</MenuItem>
|
||||||
)}
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import type { Signal } from '../../../util/signals';
|
|||||||
import {
|
import {
|
||||||
BASE_EMOJI_KEYWORD_LANG,
|
BASE_EMOJI_KEYWORD_LANG,
|
||||||
EDITABLE_INPUT_MODAL_ID,
|
EDITABLE_INPUT_MODAL_ID,
|
||||||
|
GIF_MIME_TYPE,
|
||||||
SUPPORTED_AUDIO_CONTENT_TYPES,
|
SUPPORTED_AUDIO_CONTENT_TYPES,
|
||||||
SUPPORTED_PHOTO_CONTENT_TYPES,
|
SUPPORTED_PHOTO_CONTENT_TYPES,
|
||||||
SUPPORTED_VIDEO_CONTENT_TYPES,
|
SUPPORTED_VIDEO_CONTENT_TYPES,
|
||||||
@ -154,6 +155,7 @@ const AttachmentModal = ({
|
|||||||
const svgRef = useRef<SVGSVGElement>();
|
const svgRef = useRef<SVGSVGElement>();
|
||||||
const {
|
const {
|
||||||
addRecentCustomEmoji, addRecentEmoji, updateAttachmentSettings, resetMessageMediaEditorRequest,
|
addRecentCustomEmoji, addRecentEmoji, updateAttachmentSettings, resetMessageMediaEditorRequest,
|
||||||
|
updateShouldSaveAttachmentsCompression,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -170,6 +172,7 @@ const AttachmentModal = ({
|
|||||||
const isInAlbum = editingMessage && editingMessage?.groupedId;
|
const isInAlbum = editingMessage && editingMessage?.groupedId;
|
||||||
const isEditingMessageFile = isEditing && attachments?.length && getAttachmentMediaType(attachments[0]);
|
const isEditingMessageFile = isEditing && attachments?.length && getAttachmentMediaType(attachments[0]);
|
||||||
const notEditingFile = isEditingMessageFile !== 'file';
|
const notEditingFile = isEditingMessageFile !== 'file';
|
||||||
|
const hasGifFromPicker = renderingAttachments?.some((a) => a.gif);
|
||||||
|
|
||||||
const [isSymbolMenuOpen, openSymbolMenu, closeSymbolMenu] = useFlag();
|
const [isSymbolMenuOpen, openSymbolMenu, closeSymbolMenu] = useFlag();
|
||||||
const [editingAttachmentIndex, setEditingAttachmentIndex] = useState<number | undefined>(undefined);
|
const [editingAttachmentIndex, setEditingAttachmentIndex] = useState<number | undefined>(undefined);
|
||||||
@ -185,7 +188,7 @@ const AttachmentModal = ({
|
|||||||
|
|
||||||
const shouldSendCompressed = attachmentSettings.shouldCompress;
|
const shouldSendCompressed = attachmentSettings.shouldCompress;
|
||||||
const isSendingCompressed = Boolean(
|
const isSendingCompressed = Boolean(
|
||||||
(shouldSendCompressed || shouldForceCompression || isInAlbum) && !shouldForceAsFile,
|
(shouldSendCompressed || shouldForceCompression || isInAlbum || hasGifFromPicker) && !shouldForceAsFile,
|
||||||
);
|
);
|
||||||
const [shouldSendGrouped, setShouldSendGrouped] = useState(attachmentSettings.shouldSendGrouped);
|
const [shouldSendGrouped, setShouldSendGrouped] = useState(attachmentSettings.shouldSendGrouped);
|
||||||
const isInvertedMedia = attachmentSettings.isInvertedMedia;
|
const isInvertedMedia = attachmentSettings.isInvertedMedia;
|
||||||
@ -215,6 +218,13 @@ const AttachmentModal = ({
|
|||||||
}
|
}
|
||||||
}, [closeSymbolMenu, isOpen]);
|
}, [closeSymbolMenu, isOpen]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasGifFromPicker) {
|
||||||
|
updateShouldSaveAttachmentsCompression({ shouldSave: false });
|
||||||
|
setShouldSendGrouped(false);
|
||||||
|
}
|
||||||
|
}, [hasGifFromPicker, updateShouldSaveAttachmentsCompression]);
|
||||||
|
|
||||||
const [hasMedia, hasOnlyMedia] = useMemo(() => {
|
const [hasMedia, hasOnlyMedia] = useMemo(() => {
|
||||||
const onlyMedia = Boolean(renderingAttachments?.every((a) => a.quick || a.audio));
|
const onlyMedia = Boolean(renderingAttachments?.every((a) => a.quick || a.audio));
|
||||||
if (onlyMedia) return [true, true];
|
if (onlyMedia) return [true, true];
|
||||||
@ -509,13 +519,25 @@ const AttachmentModal = ({
|
|||||||
|
|
||||||
const isQuickGallery = isSendingCompressed && hasOnlyMedia;
|
const isQuickGallery = isSendingCompressed && hasOnlyMedia;
|
||||||
|
|
||||||
const [areAllPhotos, areAllVideos, areAllAudios, hasAnyPhoto] = useMemo(() => {
|
const {
|
||||||
if (!isQuickGallery || !renderingAttachments) return [false, false, false];
|
areAllPhotos, areAllVideos, areAllAudios, areAllGifs, hasAnyPhoto,
|
||||||
const everyPhoto = renderingAttachments.every((a) => SUPPORTED_PHOTO_CONTENT_TYPES.has(a.mimeType));
|
} = useMemo(() => {
|
||||||
const everyVideo = renderingAttachments.every((a) => SUPPORTED_VIDEO_CONTENT_TYPES.has(a.mimeType));
|
if (!isQuickGallery || !renderingAttachments) {
|
||||||
const everyAudio = renderingAttachments.every((a) => SUPPORTED_AUDIO_CONTENT_TYPES.has(a.mimeType));
|
return {
|
||||||
const anyPhoto = renderingAttachments.some((a) => SUPPORTED_PHOTO_CONTENT_TYPES.has(a.mimeType));
|
areAllPhotos: false,
|
||||||
return [everyPhoto, everyVideo, everyAudio, anyPhoto];
|
areAllVideos: false,
|
||||||
|
areAllAudios: false,
|
||||||
|
areAllGifs: false,
|
||||||
|
hasAnyPhoto: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
areAllPhotos: renderingAttachments.every((a) => SUPPORTED_PHOTO_CONTENT_TYPES.has(a.mimeType)),
|
||||||
|
areAllVideos: renderingAttachments.every((a) => SUPPORTED_VIDEO_CONTENT_TYPES.has(a.mimeType)),
|
||||||
|
areAllAudios: renderingAttachments.every((a) => SUPPORTED_AUDIO_CONTENT_TYPES.has(a.mimeType)),
|
||||||
|
areAllGifs: renderingAttachments.every((a) => a.gif || a.mimeType === GIF_MIME_TYPE),
|
||||||
|
hasAnyPhoto: renderingAttachments.some((a) => SUPPORTED_PHOTO_CONTENT_TYPES.has(a.mimeType)),
|
||||||
|
};
|
||||||
}, [renderingAttachments, isQuickGallery]);
|
}, [renderingAttachments, isQuickGallery]);
|
||||||
|
|
||||||
const hasAnySpoilerable = useMemo(() => {
|
const hasAnySpoilerable = useMemo(() => {
|
||||||
@ -553,7 +575,10 @@ const AttachmentModal = ({
|
|||||||
|
|
||||||
let title = '';
|
let title = '';
|
||||||
const attachmentsLength = renderingAttachments.length;
|
const attachmentsLength = renderingAttachments.length;
|
||||||
if (areAllPhotos) {
|
|
||||||
|
if (areAllGifs) {
|
||||||
|
title = lang(isEditing ? 'AttachmentReplaceGif' : 'AttachmentSendGif');
|
||||||
|
} else if (areAllPhotos) {
|
||||||
title = lang(
|
title = lang(
|
||||||
`Attachment${isEditing ? 'Replace' : 'Send'}Photo`,
|
`Attachment${isEditing ? 'Replace' : 'Send'}Photo`,
|
||||||
{ count: attachmentsLength },
|
{ count: attachmentsLength },
|
||||||
@ -602,7 +627,7 @@ const AttachmentModal = ({
|
|||||||
trigger={MoreMenuButton}
|
trigger={MoreMenuButton}
|
||||||
positionX="right"
|
positionX="right"
|
||||||
>
|
>
|
||||||
{Boolean(!editingMessage) && (
|
{Boolean(!editingMessage) && !hasGifFromPicker && (
|
||||||
<MenuItem icon="add" onClick={handleDocumentSelect}>{lang('Add')}</MenuItem>
|
<MenuItem icon="add" onClick={handleDocumentSelect}>{lang('Add')}</MenuItem>
|
||||||
)}
|
)}
|
||||||
{hasMedia && (
|
{hasMedia && (
|
||||||
@ -621,7 +646,7 @@ const AttachmentModal = ({
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
!shouldForceAsFile && !shouldForceCompression && (isSendingCompressed ? (
|
!shouldForceAsFile && !shouldForceCompression && !hasGifFromPicker && (isSendingCompressed ? (
|
||||||
|
|
||||||
<MenuItem icon="document" onClick={handleToggleShouldCompress}>
|
<MenuItem icon="document" onClick={handleToggleShouldCompress}>
|
||||||
{lang(isMultiple ? 'AttachmentMenuSendAllAsFiles' : 'AttachmentMenuSendAsFiles')}
|
{lang(isMultiple ? 'AttachmentMenuSendAllAsFiles' : 'AttachmentMenuSendAsFiles')}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
width: calc(100% - 0.375rem);
|
||||||
height: calc(100% - 0.1875rem);
|
height: calc(100% - 0.1875rem);
|
||||||
margin: 0 0.1875rem;
|
margin: 0 0.1875rem;
|
||||||
padding-bottom: 0.1875rem;
|
padding-bottom: 0.1875rem;
|
||||||
|
|||||||
@ -24,6 +24,7 @@ type OwnProps = {
|
|||||||
loadAndPlay: boolean;
|
loadAndPlay: boolean;
|
||||||
canSendGifs?: boolean;
|
canSendGifs?: boolean;
|
||||||
onGifSelect?: (gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
onGifSelect?: (gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||||
|
onGifAddCaption?: (gif: ApiVideo) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -40,6 +41,7 @@ const GifPicker: FC<OwnProps & StateProps> = ({
|
|||||||
savedGifs,
|
savedGifs,
|
||||||
isSavedMessages,
|
isSavedMessages,
|
||||||
onGifSelect,
|
onGifSelect,
|
||||||
|
onGifAddCaption,
|
||||||
}) => {
|
}) => {
|
||||||
const { loadSavedGifs, saveGif } = getActions();
|
const { loadSavedGifs, saveGif } = getActions();
|
||||||
|
|
||||||
@ -80,9 +82,10 @@ const GifPicker: FC<OwnProps & StateProps> = ({
|
|||||||
gif={gif}
|
gif={gif}
|
||||||
observeIntersection={observeIntersection}
|
observeIntersection={observeIntersection}
|
||||||
isDisabled={!loadAndPlay}
|
isDisabled={!loadAndPlay}
|
||||||
|
isSavedMessages={isSavedMessages}
|
||||||
onClick={canSendGifs ? onGifSelect : undefined}
|
onClick={canSendGifs ? onGifSelect : undefined}
|
||||||
onUnsaveClick={handleUnsaveClick}
|
onUnsaveClick={handleUnsaveClick}
|
||||||
isSavedMessages={isSavedMessages}
|
onAddCaption={canSendGifs ? onGifAddCaption : undefined}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : canRenderContents && savedGifs ? (
|
) : canRenderContents && savedGifs ? (
|
||||||
|
|||||||
@ -56,6 +56,7 @@ export type OwnProps = {
|
|||||||
canUpdateStickerSetsOrder?: boolean,
|
canUpdateStickerSetsOrder?: boolean,
|
||||||
) => void;
|
) => void;
|
||||||
onGifSelect?: (gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
onGifSelect?: (gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||||
|
onGifAddCaption?: (gif: ApiVideo) => void;
|
||||||
onRemoveSymbol: () => void;
|
onRemoveSymbol: () => void;
|
||||||
onSearchOpen: (type: 'stickers' | 'gifs') => void;
|
onSearchOpen: (type: 'stickers' | 'gifs') => void;
|
||||||
addRecentEmoji: GlobalActions['addRecentEmoji'];
|
addRecentEmoji: GlobalActions['addRecentEmoji'];
|
||||||
@ -91,6 +92,7 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
|||||||
onCustomEmojiSelect,
|
onCustomEmojiSelect,
|
||||||
onStickerSelect,
|
onStickerSelect,
|
||||||
onGifSelect,
|
onGifSelect,
|
||||||
|
onGifAddCaption,
|
||||||
onRemoveSymbol,
|
onRemoveSymbol,
|
||||||
onSearchOpen,
|
onSearchOpen,
|
||||||
addRecentEmoji,
|
addRecentEmoji,
|
||||||
@ -239,6 +241,7 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
|||||||
loadAndPlay={canSendGifs ? isOpen && (isActive || isFrom) : false}
|
loadAndPlay={canSendGifs ? isOpen && (isActive || isFrom) : false}
|
||||||
canSendGifs={canSendGifs}
|
canSendGifs={canSendGifs}
|
||||||
onGifSelect={onGifSelect}
|
onGifSelect={onGifSelect}
|
||||||
|
onGifAddCaption={onGifAddCaption}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,7 @@ type OwnProps = {
|
|||||||
canUpdateStickerSetsOrder?: boolean,
|
canUpdateStickerSetsOrder?: boolean,
|
||||||
) => void;
|
) => void;
|
||||||
onGifSelect?: (gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
onGifSelect?: (gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||||
|
onGifAddCaption?: (gif: ApiVideo) => void;
|
||||||
onRemoveSymbol: VoidFunction;
|
onRemoveSymbol: VoidFunction;
|
||||||
onEmojiSelect: (emoji: string) => void;
|
onEmojiSelect: (emoji: string) => void;
|
||||||
closeBotCommandMenu?: VoidFunction;
|
closeBotCommandMenu?: VoidFunction;
|
||||||
@ -73,6 +74,7 @@ const SymbolMenuButton: FC<OwnProps> = ({
|
|||||||
onCustomEmojiSelect,
|
onCustomEmojiSelect,
|
||||||
onStickerSelect,
|
onStickerSelect,
|
||||||
onGifSelect,
|
onGifSelect,
|
||||||
|
onGifAddCaption,
|
||||||
onRemoveSymbol,
|
onRemoveSymbol,
|
||||||
onEmojiSelect,
|
onEmojiSelect,
|
||||||
closeBotCommandMenu,
|
closeBotCommandMenu,
|
||||||
@ -181,6 +183,7 @@ const SymbolMenuButton: FC<OwnProps> = ({
|
|||||||
onStickerSelect={onStickerSelect}
|
onStickerSelect={onStickerSelect}
|
||||||
onCustomEmojiSelect={onCustomEmojiSelect}
|
onCustomEmojiSelect={onCustomEmojiSelect}
|
||||||
onGifSelect={onGifSelect}
|
onGifSelect={onGifSelect}
|
||||||
|
onGifAddCaption={onGifAddCaption}
|
||||||
onRemoveSymbol={onRemoveSymbol}
|
onRemoveSymbol={onRemoveSymbol}
|
||||||
onSearchOpen={handleSearchOpen}
|
onSearchOpen={handleSearchOpen}
|
||||||
addRecentEmoji={addRecentEmoji}
|
addRecentEmoji={addRecentEmoji}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { ApiAttachment } from '../../../../api/types';
|
import type { ApiAttachment, ApiVideo } from '../../../../api/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GIF_MIME_TYPE,
|
GIF_MIME_TYPE,
|
||||||
@ -132,3 +132,26 @@ function validateAspectRatio(width: number, height: number) {
|
|||||||
const maxAspectRatio = Math.max(width, height) / Math.min(width, height);
|
const maxAspectRatio = Math.max(width, height) / Math.min(width, height);
|
||||||
return maxAspectRatio <= MAX_ASPECT_RATIO;
|
return maxAspectRatio <= MAX_ASPECT_RATIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildGifAttachment(gif: ApiVideo): ApiAttachment {
|
||||||
|
const {
|
||||||
|
blobUrl,
|
||||||
|
thumbnail,
|
||||||
|
fileName,
|
||||||
|
mimeType,
|
||||||
|
size,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
duration,
|
||||||
|
} = gif;
|
||||||
|
|
||||||
|
return {
|
||||||
|
gif,
|
||||||
|
blobUrl: blobUrl || '',
|
||||||
|
previewBlobUrl: thumbnail?.dataUri,
|
||||||
|
filename: fileName,
|
||||||
|
mimeType,
|
||||||
|
size,
|
||||||
|
quick: width && height ? { width, height, duration } : undefined,
|
||||||
|
} satisfies ApiAttachment;
|
||||||
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { getActions } from '../../../../global';
|
|||||||
|
|
||||||
import type { ApiAttachment, ApiMessage } from '../../../../api/types';
|
import type { ApiAttachment, ApiMessage } from '../../../../api/types';
|
||||||
|
|
||||||
|
import { GIF_MIME_TYPE } from '../../../../config';
|
||||||
import { canReplaceMessageMedia, getAttachmentMediaType } from '../../../../global/helpers';
|
import { canReplaceMessageMedia, getAttachmentMediaType } from '../../../../global/helpers';
|
||||||
import { MEMO_EMPTY_ARRAY } from '../../../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../../../util/memo';
|
||||||
import buildAttachment from '../helpers/buildAttachment';
|
import buildAttachment from '../helpers/buildAttachment';
|
||||||
@ -86,6 +87,11 @@ export default function useAttachmentModal({
|
|||||||
|
|
||||||
const handleAppendFiles = useLastCallback(async (files: File[], isSpoiler?: boolean) => {
|
const handleAppendFiles = useLastCallback(async (files: File[], isSpoiler?: boolean) => {
|
||||||
if (editedMessage) {
|
if (editedMessage) {
|
||||||
|
if (editedMessage.groupedId && files[0].type === GIF_MIME_TYPE) {
|
||||||
|
showNotification({ message: lang('MediaReplaceInvalidError', undefined, { pluralValue: 1 }) });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const newAttachment = await buildAttachment(files[0].name, files[0]);
|
const newAttachment = await buildAttachment(files[0].name, files[0]);
|
||||||
const canReplace = editedMessage && canReplaceMessageMedia(editedMessage, newAttachment);
|
const canReplace = editedMessage && canReplaceMessageMedia(editedMessage, newAttachment);
|
||||||
|
|
||||||
@ -109,6 +115,11 @@ export default function useAttachmentModal({
|
|||||||
|
|
||||||
const handleFileSelect = useLastCallback(async (files: File[]) => {
|
const handleFileSelect = useLastCallback(async (files: File[]) => {
|
||||||
if (editedMessage) {
|
if (editedMessage) {
|
||||||
|
if (editedMessage.groupedId && files[0].type === GIF_MIME_TYPE) {
|
||||||
|
showNotification({ message: lang('MediaReplaceInvalidError', undefined, { pluralValue: 1 }) });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const newAttachment = await buildAttachment(files[0].name, files[0]);
|
const newAttachment = await buildAttachment(files[0].name, files[0]);
|
||||||
const canReplace = editedMessage && canReplaceMessageMedia(editedMessage, newAttachment);
|
const canReplace = editedMessage && canReplaceMessageMedia(editedMessage, newAttachment);
|
||||||
|
|
||||||
@ -129,8 +140,10 @@ export default function useAttachmentModal({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleUpdateAttachmentsQuality = useLastCallback(async () => {
|
const handleUpdateAttachmentsQuality = useLastCallback(async () => {
|
||||||
const newAttachments = await Promise.all(attachments.map((attachment) =>
|
const newAttachments = await Promise.all(attachments.map(async (attachment) => {
|
||||||
buildAttachment(attachment.filename, attachment.blob, { shouldSendInHighQuality })));
|
if (!attachment.blob) return attachment;
|
||||||
|
return buildAttachment(attachment.filename, attachment.blob, { shouldSendInHighQuality });
|
||||||
|
}));
|
||||||
handleSetAttachments(newAttachments);
|
handleSetAttachments(newAttachments);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@ const useClipboardPaste = (
|
|||||||
shouldStripCustomEmoji?: boolean,
|
shouldStripCustomEmoji?: boolean,
|
||||||
onCustomEmojiStripped?: VoidFunction,
|
onCustomEmojiStripped?: VoidFunction,
|
||||||
shouldUpdateAttachmentCompression?: boolean,
|
shouldUpdateAttachmentCompression?: boolean,
|
||||||
|
shouldSkipFilePaste?: boolean,
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
showNotification,
|
showNotification,
|
||||||
@ -100,7 +101,7 @@ const useClipboardPaste = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasText = textToPaste && textToPaste.text;
|
const hasText = textToPaste && textToPaste.text;
|
||||||
let shouldSetAttachments = files?.length && !isWordDocument;
|
let shouldSetAttachments = files?.length && !isWordDocument && !shouldSkipFilePaste;
|
||||||
|
|
||||||
const newAttachments = files ? await Promise.all(files.map((file) => buildAttachment(file.name, file))) : [];
|
const newAttachments = files ? await Promise.all(files.map((file) => buildAttachment(file.name, file))) : [];
|
||||||
const canReplace = (editedMessage && newAttachments?.length
|
const canReplace = (editedMessage && newAttachments?.length
|
||||||
@ -154,7 +155,7 @@ const useClipboardPaste = (
|
|||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
insertTextAndUpdateCursor, editedMessage, setAttachments, isActive, shouldStripCustomEmoji,
|
insertTextAndUpdateCursor, editedMessage, setAttachments, isActive, shouldStripCustomEmoji,
|
||||||
onCustomEmojiStripped, setNextText, lang, shouldUpdateAttachmentCompression,
|
onCustomEmojiStripped, setNextText, lang, shouldUpdateAttachmentCompression, shouldSkipFilePaste,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -299,14 +299,16 @@ const Video = <T,>({
|
|||||||
style={forcedWidth ? `width: ${forcedWidth}px` : undefined}
|
style={forcedWidth ? `width: ${forcedWidth}px` : undefined}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<img
|
{previewBlobUrl && (
|
||||||
ref={previewRef}
|
<img
|
||||||
src={previewBlobUrl}
|
ref={previewRef}
|
||||||
className={buildClassName('thumbnail', withBlurredBackground && 'with-blurred-bg')}
|
src={previewBlobUrl}
|
||||||
alt=""
|
className={buildClassName('thumbnail', withBlurredBackground && 'with-blurred-bg')}
|
||||||
style={forcedWidth ? `width: ${forcedWidth}px;` : undefined}
|
alt=""
|
||||||
draggable={!isProtected}
|
style={forcedWidth ? `width: ${forcedWidth}px;` : undefined}
|
||||||
/>
|
draggable={!isProtected}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{hasThumb && !isPreviewPreloaded && (
|
{hasThumb && !isPreviewPreloaded && (
|
||||||
<canvas ref={thumbRef} className="thumbnail" />
|
<canvas ref={thumbRef} className="thumbnail" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
src: url("./icons.woff2?d1a9cacb64e401206f928a39a587a2bd") format("woff2"),
|
src: url("./icons.woff2?c37e5ad179a86df12e2f9214af6a855a") format("woff2"),
|
||||||
url("./icons.woff?d1a9cacb64e401206f928a39a587a2bd") format("woff");
|
url("./icons.woff?c37e5ad179a86df12e2f9214af6a855a") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-char::before {
|
.icon-char::before {
|
||||||
@ -900,87 +900,90 @@ url("./icons.woff?d1a9cacb64e401206f928a39a587a2bd") format("woff");
|
|||||||
.icon-add-filled::before {
|
.icon-add-filled::before {
|
||||||
content: "\f225";
|
content: "\f225";
|
||||||
}
|
}
|
||||||
.icon-active-sessions::before {
|
.icon-add-caption::before {
|
||||||
content: "\f226";
|
content: "\f226";
|
||||||
}
|
}
|
||||||
.icon-folder-tabs-user::before {
|
.icon-active-sessions::before {
|
||||||
content: "\f227";
|
content: "\f227";
|
||||||
}
|
}
|
||||||
.icon-folder-tabs-star::before {
|
.icon-rating-icons-negative::before {
|
||||||
content: "\f228";
|
content: "\f228";
|
||||||
}
|
}
|
||||||
.icon-folder-tabs-group::before {
|
.icon-rating-icons-level90::before {
|
||||||
content: "\f229";
|
content: "\f229";
|
||||||
}
|
}
|
||||||
.icon-folder-tabs-folder::before {
|
.icon-rating-icons-level9::before {
|
||||||
content: "\f22a";
|
content: "\f22a";
|
||||||
}
|
}
|
||||||
.icon-folder-tabs-chats::before {
|
.icon-rating-icons-level80::before {
|
||||||
content: "\f22b";
|
content: "\f22b";
|
||||||
}
|
}
|
||||||
.icon-folder-tabs-chat::before {
|
.icon-rating-icons-level8::before {
|
||||||
content: "\f22c";
|
content: "\f22c";
|
||||||
}
|
}
|
||||||
.icon-folder-tabs-channel::before {
|
.icon-rating-icons-level70::before {
|
||||||
content: "\f22d";
|
content: "\f22d";
|
||||||
}
|
}
|
||||||
.icon-folder-tabs-bot::before {
|
.icon-rating-icons-level7::before {
|
||||||
content: "\f22e";
|
content: "\f22e";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-negative::before {
|
.icon-rating-icons-level60::before {
|
||||||
content: "\f22f";
|
content: "\f22f";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level90::before {
|
.icon-rating-icons-level6::before {
|
||||||
content: "\f230";
|
content: "\f230";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level9::before {
|
.icon-rating-icons-level50::before {
|
||||||
content: "\f231";
|
content: "\f231";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level80::before {
|
.icon-rating-icons-level5::before {
|
||||||
content: "\f232";
|
content: "\f232";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level8::before {
|
.icon-rating-icons-level40::before {
|
||||||
content: "\f233";
|
content: "\f233";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level70::before {
|
.icon-rating-icons-level4::before {
|
||||||
content: "\f234";
|
content: "\f234";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level7::before {
|
.icon-rating-icons-level30::before {
|
||||||
content: "\f235";
|
content: "\f235";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level60::before {
|
.icon-rating-icons-level3::before {
|
||||||
content: "\f236";
|
content: "\f236";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level6::before {
|
.icon-rating-icons-level20::before {
|
||||||
content: "\f237";
|
content: "\f237";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level50::before {
|
.icon-rating-icons-level2::before {
|
||||||
content: "\f238";
|
content: "\f238";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level5::before {
|
.icon-rating-icons-level10::before {
|
||||||
content: "\f239";
|
content: "\f239";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level40::before {
|
.icon-rating-icons-level1::before {
|
||||||
content: "\f23a";
|
content: "\f23a";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level4::before {
|
.icon-folder-tabs-user::before {
|
||||||
content: "\f23b";
|
content: "\f23b";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level30::before {
|
.icon-folder-tabs-star::before {
|
||||||
content: "\f23c";
|
content: "\f23c";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level3::before {
|
.icon-folder-tabs-group::before {
|
||||||
content: "\f23d";
|
content: "\f23d";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level20::before {
|
.icon-folder-tabs-folder::before {
|
||||||
content: "\f23e";
|
content: "\f23e";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level2::before {
|
.icon-folder-tabs-chats::before {
|
||||||
content: "\f23f";
|
content: "\f23f";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level10::before {
|
.icon-folder-tabs-chat::before {
|
||||||
content: "\f240";
|
content: "\f240";
|
||||||
}
|
}
|
||||||
.icon-rating-icons-level1::before {
|
.icon-folder-tabs-channel::before {
|
||||||
content: "\f241";
|
content: "\f241";
|
||||||
}
|
}
|
||||||
|
.icon-folder-tabs-bot::before {
|
||||||
|
content: "\f242";
|
||||||
|
}
|
||||||
|
|||||||
@ -309,32 +309,33 @@ $icons-map: (
|
|||||||
"add-user-filled": "\f223",
|
"add-user-filled": "\f223",
|
||||||
"add-one-badge": "\f224",
|
"add-one-badge": "\f224",
|
||||||
"add-filled": "\f225",
|
"add-filled": "\f225",
|
||||||
"active-sessions": "\f226",
|
"add-caption": "\f226",
|
||||||
"folder-tabs-user": "\f227",
|
"active-sessions": "\f227",
|
||||||
"folder-tabs-star": "\f228",
|
"rating-icons-negative": "\f228",
|
||||||
"folder-tabs-group": "\f229",
|
"rating-icons-level90": "\f229",
|
||||||
"folder-tabs-folder": "\f22a",
|
"rating-icons-level9": "\f22a",
|
||||||
"folder-tabs-chats": "\f22b",
|
"rating-icons-level80": "\f22b",
|
||||||
"folder-tabs-chat": "\f22c",
|
"rating-icons-level8": "\f22c",
|
||||||
"folder-tabs-channel": "\f22d",
|
"rating-icons-level70": "\f22d",
|
||||||
"folder-tabs-bot": "\f22e",
|
"rating-icons-level7": "\f22e",
|
||||||
"rating-icons-negative": "\f22f",
|
"rating-icons-level60": "\f22f",
|
||||||
"rating-icons-level90": "\f230",
|
"rating-icons-level6": "\f230",
|
||||||
"rating-icons-level9": "\f231",
|
"rating-icons-level50": "\f231",
|
||||||
"rating-icons-level80": "\f232",
|
"rating-icons-level5": "\f232",
|
||||||
"rating-icons-level8": "\f233",
|
"rating-icons-level40": "\f233",
|
||||||
"rating-icons-level70": "\f234",
|
"rating-icons-level4": "\f234",
|
||||||
"rating-icons-level7": "\f235",
|
"rating-icons-level30": "\f235",
|
||||||
"rating-icons-level60": "\f236",
|
"rating-icons-level3": "\f236",
|
||||||
"rating-icons-level6": "\f237",
|
"rating-icons-level20": "\f237",
|
||||||
"rating-icons-level50": "\f238",
|
"rating-icons-level2": "\f238",
|
||||||
"rating-icons-level5": "\f239",
|
"rating-icons-level10": "\f239",
|
||||||
"rating-icons-level40": "\f23a",
|
"rating-icons-level1": "\f23a",
|
||||||
"rating-icons-level4": "\f23b",
|
"folder-tabs-user": "\f23b",
|
||||||
"rating-icons-level30": "\f23c",
|
"folder-tabs-star": "\f23c",
|
||||||
"rating-icons-level3": "\f23d",
|
"folder-tabs-group": "\f23d",
|
||||||
"rating-icons-level20": "\f23e",
|
"folder-tabs-folder": "\f23e",
|
||||||
"rating-icons-level2": "\f23f",
|
"folder-tabs-chats": "\f23f",
|
||||||
"rating-icons-level10": "\f240",
|
"folder-tabs-chat": "\f240",
|
||||||
"rating-icons-level1": "\f241",
|
"folder-tabs-channel": "\f241",
|
||||||
|
"folder-tabs-bot": "\f242",
|
||||||
);
|
);
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -292,15 +292,8 @@ export type FontIconName =
|
|||||||
| 'add-user-filled'
|
| 'add-user-filled'
|
||||||
| 'add-one-badge'
|
| 'add-one-badge'
|
||||||
| 'add-filled'
|
| 'add-filled'
|
||||||
|
| 'add-caption'
|
||||||
| 'active-sessions'
|
| '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-negative'
|
||||||
| 'rating-icons-level90'
|
| 'rating-icons-level90'
|
||||||
| 'rating-icons-level9'
|
| 'rating-icons-level9'
|
||||||
@ -319,4 +312,12 @@ export type FontIconName =
|
|||||||
| 'rating-icons-level20'
|
| 'rating-icons-level20'
|
||||||
| 'rating-icons-level2'
|
| 'rating-icons-level2'
|
||||||
| 'rating-icons-level10'
|
| 'rating-icons-level10'
|
||||||
| 'rating-icons-level1';
|
| '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';
|
||||||
|
|||||||
3
src/types/language.d.ts
vendored
3
src/types/language.d.ts
vendored
@ -1958,6 +1958,8 @@ export interface LangPair {
|
|||||||
'AttachmentMenuUngroupAllMedia': undefined;
|
'AttachmentMenuUngroupAllMedia': undefined;
|
||||||
'AttachmentMenuEnableSpoiler': undefined;
|
'AttachmentMenuEnableSpoiler': undefined;
|
||||||
'AttachmentMenuDisableSpoiler': undefined;
|
'AttachmentMenuDisableSpoiler': undefined;
|
||||||
|
'AttachmentSendGif': undefined;
|
||||||
|
'AttachmentReplaceGif': undefined;
|
||||||
'AttachmentDragAddItems': undefined;
|
'AttachmentDragAddItems': undefined;
|
||||||
'AttachmentCaptionPlaceholder': undefined;
|
'AttachmentCaptionPlaceholder': undefined;
|
||||||
'MessageSummaryTitle': undefined;
|
'MessageSummaryTitle': undefined;
|
||||||
@ -2002,6 +2004,7 @@ export interface LangPair {
|
|||||||
'GiftPreviewToggleRegularModels': undefined;
|
'GiftPreviewToggleRegularModels': undefined;
|
||||||
'AriaGiftPreviewPlay': undefined;
|
'AriaGiftPreviewPlay': undefined;
|
||||||
'AriaGiftPreviewStop': undefined;
|
'AriaGiftPreviewStop': undefined;
|
||||||
|
'MenuAddCaption': undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LangPairWithVariables<V = LangVariable> {
|
export interface LangPairWithVariables<V = LangVariable> {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user