diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts
index d444d27af..3ff76dd4f 100644
--- a/src/api/types/misc.ts
+++ b/src/api/types/misc.ts
@@ -36,6 +36,7 @@ export interface ApiOnProgress {
}
export interface ApiAttachment {
+ blob: Blob;
blobUrl: string;
compressedBlobUrl?: string;
filename: string;
@@ -62,6 +63,7 @@ export interface ApiAttachment {
uniqueId?: string;
ttlSeconds?: number;
+ shouldSendInHighQuality?: boolean;
}
export interface ApiWallpaper {
diff --git a/src/assets/font-icons/hd-photo.svg b/src/assets/font-icons/hd-photo.svg
new file mode 100644
index 000000000..5a24da81b
--- /dev/null
+++ b/src/assets/font-icons/hd-photo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/font-icons/sd-photo.svg b/src/assets/font-icons/sd-photo.svg
new file mode 100644
index 000000000..d71450b1e
--- /dev/null
+++ b/src/assets/font-icons/sd-photo.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 fbc9832b7..dedab41c7 100644
--- a/src/assets/localization/fallback.strings
+++ b/src/assets/localization/fallback.strings
@@ -2017,6 +2017,8 @@
"ValueGiftSortByNumber" = "Number";
"ResellGiftsNoFound" = "No gifts found";
"ResellGiftsClearFilters" = "Clear Filters";
+"SendInStandardQuality" = "Send In Standard Quality";
+"SendInHighQuality" = "Send In High Quality";
"MonoforumBadge" = "DIRECT";
"MonoforumStatus" = "Channel messages";
"MonoforumComposerPlaceholder" = "Choose a message to reply";
diff --git a/src/components/common/Composer.tsx b/src/components/common/Composer.tsx
index 530009245..bd58ebaba 100644
--- a/src/components/common/Composer.tsx
+++ b/src/components/common/Composer.tsx
@@ -635,7 +635,6 @@ const Composer: FC = ({
});
const {
- shouldSuggestCompression,
shouldForceCompression,
shouldForceAsFile,
handleAppendFiles,
@@ -655,6 +654,7 @@ const Composer: FC = ({
canSendDocuments,
insertNextText,
editedMessage: editingMessage,
+ shouldSendInHighQuality: attachmentSettings.shouldSendInHighQuality,
});
const [isBotKeyboardOpen, openBotKeyboard, closeBotKeyboard] = useFlag();
@@ -1848,7 +1848,6 @@ const Composer: FC = ({
attachments={attachments}
getHtml={getHtml}
isReady={isReady}
- shouldSuggestCompression={shouldSuggestCompression}
shouldForceCompression={shouldForceCompression}
shouldForceAsFile={shouldForceAsFile}
isForCurrentMessageList={isForCurrentMessageList}
diff --git a/src/components/middle/composer/AttachMenu.tsx b/src/components/middle/composer/AttachMenu.tsx
index f13b269ee..bfeddbf9a 100644
--- a/src/components/middle/composer/AttachMenu.tsx
+++ b/src/components/middle/composer/AttachMenu.tsx
@@ -3,6 +3,7 @@ import {
memo, useEffect,
useMemo,
} from '../../../lib/teact/teact';
+import { getActions } from '../../../global';
import type { ApiAttachMenuPeerType, ApiMessage } from '../../../api/types';
import type { GlobalState } from '../../../global/types';
@@ -55,7 +56,7 @@ export type OwnProps = {
peerType?: ApiAttachMenuPeerType;
shouldCollectDebugLogs?: boolean;
theme: ThemeKey;
- onFileSelect: (files: File[], shouldSuggestCompression?: boolean) => void;
+ onFileSelect: (files: File[]) => void;
onPollCreate: NoneToVoidFunction;
onMenuOpen: NoneToVoidFunction;
onMenuClose: NoneToVoidFunction;
@@ -89,6 +90,9 @@ const AttachMenu: FC = ({
messageListType,
paidMessagesStars,
}) => {
+ const {
+ updateAttachmentSettings,
+ } = getActions();
const [isAttachMenuOpen, openAttachMenu, closeAttachMenu] = useFlag();
const [handleMouseEnter, handleMouseLeave, markMouseInside] = useMouseInside(isAttachMenuOpen, closeAttachMenu);
@@ -126,29 +130,31 @@ const AttachMenu: FC = ({
}
});
- const handleFileSelect = useLastCallback((e: Event, shouldSuggestCompression?: boolean) => {
+ const handleFileSelect = useLastCallback((e: Event) => {
const { files } = e.target as HTMLInputElement;
const validatedFiles = validateFiles(files);
if (validatedFiles?.length) {
- onFileSelect(validatedFiles, shouldSuggestCompression);
+ onFileSelect(validatedFiles);
}
});
const handleQuickSelect = useLastCallback(() => {
+ updateAttachmentSettings({ shouldCompress: true });
openSystemFilesDialog(
Array.from(canSendVideoAndPhoto ? CONTENT_TYPES_WITH_PREVIEW : (
canSendPhotos ? SUPPORTED_PHOTO_CONTENT_TYPES : SUPPORTED_VIDEO_CONTENT_TYPES
)).join(','),
- (e) => handleFileSelect(e, true),
+ (e) => handleFileSelect(e),
);
});
const handleDocumentSelect = useLastCallback(() => {
+ updateAttachmentSettings({ shouldCompress: false });
openSystemFilesDialog(!canSendDocuments && canSendAudios
? Array.from(SUPPORTED_AUDIO_CONTENT_TYPES).join(',') : (
'*'
- ), (e) => handleFileSelect(e, false));
+ ), (e) => handleFileSelect(e));
});
const handleSendLogs = useLastCallback(() => {
diff --git a/src/components/middle/composer/AttachmentModal.tsx b/src/components/middle/composer/AttachmentModal.tsx
index ca86e3b23..fafec7638 100644
--- a/src/components/middle/composer/AttachmentModal.tsx
+++ b/src/components/middle/composer/AttachmentModal.tsx
@@ -75,7 +75,6 @@ export type OwnProps = {
isReady: boolean;
isForMessage?: boolean;
shouldSchedule?: boolean;
- shouldSuggestCompression?: boolean;
shouldForceCompression?: boolean;
shouldForceAsFile?: boolean;
isForCurrentMessageList?: boolean;
@@ -113,6 +112,7 @@ type StateProps = {
const ATTACHMENT_MODAL_INPUT_ID = 'caption-input-text';
const DROP_LEAVE_TIMEOUT_MS = 150;
const MAX_LEFT_CHARS_TO_SHOW = 100;
+const CLOSE_MENU_ANIMATION_DURATION = 200;
const AttachmentModal: FC = ({
chatId,
@@ -134,7 +134,6 @@ const AttachmentModal: FC = ({
shouldSuggestCustomEmoji,
customEmojiForEmoji,
attachmentSettings,
- shouldSuggestCompression,
shouldForceCompression,
shouldForceAsFile,
isForCurrentMessageList,
@@ -176,14 +175,16 @@ const AttachmentModal: FC = ({
const [isSymbolMenuOpen, openSymbolMenu, closeSymbolMenu] = useFlag();
- const [shouldSendCompressed, setShouldSendCompressed] = useState(
- shouldSuggestCompression ?? attachmentSettings.shouldCompress,
- );
+ const shouldSendCompressed = attachmentSettings.shouldCompress;
const isSendingCompressed = Boolean(
(shouldSendCompressed || shouldForceCompression || isInAlbum) && !shouldForceAsFile,
);
const [shouldSendGrouped, setShouldSendGrouped] = useState(attachmentSettings.shouldSendGrouped);
const isInvertedMedia = attachmentSettings.isInvertedMedia;
+ const [shouldSendInHighQuality, setShouldSendInHighQuality] = useState(
+ attachmentSettings.shouldSendInHighQuality,
+ );
+ const [renderingShouldSendInHighQuality, setRenderingShouldSendInHighQuality] = useState(shouldSendInHighQuality);
const {
handleScroll: handleAttachmentsScroll,
@@ -197,6 +198,8 @@ const AttachmentModal: FC = ({
const renderingIsOpen = Boolean(renderingAttachments?.length);
const [isHovered, markHovered, unmarkHovered] = useFlag();
+ const timerRef = useRef();
+
useEffect(() => {
if (!isOpen) {
closeSymbolMenu();
@@ -269,16 +272,16 @@ const AttachmentModal: FC = ({
useEffect(() => {
if (isOpen) {
- setShouldSendCompressed(shouldSuggestCompression ?? attachmentSettings.shouldCompress);
setShouldSendGrouped(attachmentSettings.shouldSendGrouped);
+ setShouldSendInHighQuality(attachmentSettings.shouldSendInHighQuality);
}
- }, [attachmentSettings, isOpen, shouldSuggestCompression]);
+ }, [attachmentSettings, isOpen]);
useEffect(() => {
if (!isOpen) {
updateAttachmentSettings({ isInvertedMedia: undefined });
}
- }, [updateAttachmentSettings, isOpen, shouldSuggestCompression]);
+ }, [updateAttachmentSettings, isOpen]);
function setIsInvertedMedia(value?: true) {
updateAttachmentSettings({ isInvertedMedia: value });
@@ -303,9 +306,10 @@ const AttachmentModal: FC = ({
: isSilent ? onSendSilent : onSend;
send(isSendingCompressed, shouldSendGrouped, isInvertedMedia);
updateAttachmentSettings({
- shouldCompress: shouldSuggestCompression === undefined ? isSendingCompressed : undefined,
+ shouldCompress: isSendingCompressed,
shouldSendGrouped,
isInvertedMedia,
+ shouldSendInHighQuality,
});
}
});
@@ -387,6 +391,17 @@ const AttachmentModal: FC = ({
})));
});
+ const handleToggleShouldCompress = useLastCallback(() => {
+ const newValue = !shouldSendCompressed;
+ updateAttachmentSettings({ shouldCompress: newValue });
+ });
+
+ const handleToggleQuality = useLastCallback(() => {
+ const newValue = !shouldSendInHighQuality;
+ setShouldSendInHighQuality(newValue);
+ updateAttachmentSettings({ shouldSendInHighQuality: newValue });
+ });
+
const handleDisableSpoilers = useLastCallback(() => {
onAttachmentsUpdate(attachments.map((a) => ({ ...a, shouldSendAsSpoiler: undefined })));
});
@@ -458,12 +473,13 @@ const AttachmentModal: FC = ({
const isQuickGallery = isSendingCompressed && hasOnlyMedia;
- const [areAllPhotos, areAllVideos, areAllAudios] = useMemo(() => {
+ const [areAllPhotos, areAllVideos, areAllAudios, hasAnyPhoto] = useMemo(() => {
if (!isQuickGallery || !renderingAttachments) return [false, false, false];
const everyPhoto = renderingAttachments.every((a) => SUPPORTED_PHOTO_CONTENT_TYPES.has(a.mimeType));
const everyVideo = renderingAttachments.every((a) => SUPPORTED_VIDEO_CONTENT_TYPES.has(a.mimeType));
const everyAudio = renderingAttachments.every((a) => SUPPORTED_AUDIO_CONTENT_TYPES.has(a.mimeType));
- return [everyPhoto, everyVideo, everyAudio];
+ const hasAnyPhoto = renderingAttachments.some((a) => SUPPORTED_PHOTO_CONTENT_TYPES.has(a.mimeType));
+ return [everyPhoto, everyVideo, everyAudio, hasAnyPhoto];
}, [renderingAttachments, isQuickGallery]);
const hasAnySpoilerable = useMemo(() => {
@@ -471,6 +487,20 @@ const AttachmentModal: FC = ({
return renderingAttachments.some((a) => !SUPPORTED_AUDIO_CONTENT_TYPES.has(a.mimeType));
}, [renderingAttachments]);
+ useEffect(() => {
+ if (shouldSendInHighQuality === renderingShouldSendInHighQuality) return;
+ if (timerRef.current) clearTimeout(timerRef.current);
+ timerRef.current = window.setTimeout(() => {
+ setRenderingShouldSendInHighQuality(shouldSendInHighQuality);
+ }, CLOSE_MENU_ANIMATION_DURATION);
+ return () => {
+ if (timerRef.current) {
+ clearTimeout(timerRef.current);
+ timerRef.current = undefined;
+ }
+ };
+ }, [shouldSendInHighQuality, renderingShouldSendInHighQuality]);
+
if (!renderingAttachments) {
return undefined;
}
@@ -536,16 +566,24 @@ const AttachmentModal: FC = ({
{
!shouldForceAsFile && !shouldForceCompression && (isSendingCompressed ? (
-