SVG: Ask before download (#3872)

This commit is contained in:
Alexander Zinchuk 2023-09-25 13:00:11 +02:00
parent f8c021b043
commit bcb1a7ae26
7 changed files with 82 additions and 27 deletions

View File

@ -22,10 +22,13 @@ import { getDocumentExtension, getDocumentHasPreview } from './helpers/documentI
import useFlag from '../../hooks/useFlag'; import useFlag from '../../hooks/useFlag';
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 useMediaWithLoadProgress from '../../hooks/useMediaWithLoadProgress'; import useMediaWithLoadProgress from '../../hooks/useMediaWithLoadProgress';
import Checkbox from '../ui/Checkbox';
import ConfirmDialog from '../ui/ConfirmDialog';
import File from './File'; import File from './File';
type OwnProps = { type OwnProps = {
@ -42,12 +45,14 @@ type OwnProps = {
sender?: string; sender?: string;
autoLoadFileMaxSizeMb?: number; autoLoadFileMaxSizeMb?: number;
isDownloading?: boolean; isDownloading?: boolean;
shouldWarnAboutSvg?: boolean;
onCancelUpload?: () => void; onCancelUpload?: () => void;
onMediaClick?: () => void; onMediaClick?: () => void;
onDateClick?: (messageId: number, chatId: string) => void; onDateClick?: (messageId: number, chatId: string) => void;
}; };
const BYTES_PER_MB = 1024 * 1024; const BYTES_PER_MB = 1024 * 1024;
const SVG_EXTENSIONS = new Set(['svg', 'svgz']);
const Document: FC<OwnProps> = ({ const Document: FC<OwnProps> = ({
message, message,
@ -62,16 +67,21 @@ const Document: FC<OwnProps> = ({
sender, sender,
isSelected, isSelected,
isSelectable, isSelectable,
shouldWarnAboutSvg,
isDownloading,
onCancelUpload, onCancelUpload,
onMediaClick, onMediaClick,
onDateClick, onDateClick,
isDownloading,
}) => { }) => {
const dispatch = getActions(); const { cancelMessageMediaDownload, downloadMessageMedia, setSettingOption } = getActions();
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const lang = useLang();
const [isSvgDialogOpen, openSvgDialog, closeSvgDialog] = useFlag();
const [shouldNotWarnAboutSvg, setShouldNotWarnAboutSvg] = useState(false);
const document = message.content.document!; const document = message.content.document!;
const { fileName, size, timestamp } = document; const { fileName, size, timestamp } = document;
const extension = getDocumentExtension(document) || ''; const extension = getDocumentExtension(document) || '';
@ -110,6 +120,10 @@ const Document: FC<OwnProps> = ({
SUPPORTED_VIDEO_CONTENT_TYPES.has(document.mimeType) || SUPPORTED_IMAGE_CONTENT_TYPES.has(document.mimeType) SUPPORTED_VIDEO_CONTENT_TYPES.has(document.mimeType) || SUPPORTED_IMAGE_CONTENT_TYPES.has(document.mimeType)
); );
const handleDownload = useLastCallback(() => {
downloadMessageMedia({ message });
});
const handleClick = useLastCallback(() => { const handleClick = useLastCallback(() => {
if (isUploading) { if (isUploading) {
if (onCancelUpload) { if (onCancelUpload) {
@ -119,7 +133,7 @@ const Document: FC<OwnProps> = ({
} }
if (isDownloading) { if (isDownloading) {
dispatch.cancelMessageMediaDownload({ message }); cancelMessageMediaDownload({ message });
return; return;
} }
@ -130,9 +144,21 @@ const Document: FC<OwnProps> = ({
if (withMediaViewer) { if (withMediaViewer) {
onMediaClick!(); onMediaClick!();
} else { return;
dispatch.downloadMessageMedia({ message });
} }
if (SVG_EXTENSIONS.has(extension) && shouldWarnAboutSvg) {
openSvgDialog();
return;
}
handleDownload();
});
const handleSvgConfirm = useLastCallback(() => {
setSettingOption({ shouldWarnAboutSvg: !shouldNotWarnAboutSvg });
closeSvgDialog();
handleDownload();
}); });
const handleDateClick = useLastCallback(() => { const handleDateClick = useLastCallback(() => {
@ -140,26 +166,41 @@ const Document: FC<OwnProps> = ({
}); });
return ( return (
<File <>
ref={ref} <File
name={fileName} ref={ref}
extension={extension} name={fileName}
size={size} extension={extension}
timestamp={withDate ? datetime || timestamp : undefined} size={size}
thumbnailDataUri={thumbDataUri} timestamp={withDate ? datetime || timestamp : undefined}
previewData={localBlobUrl || previewData} thumbnailDataUri={thumbDataUri}
smaller={smaller} previewData={localBlobUrl || previewData}
isTransferring={isTransferring} smaller={smaller}
isUploading={isUploading} isTransferring={isTransferring}
transferProgress={transferProgress} isUploading={isUploading}
className={className} transferProgress={transferProgress}
sender={sender} className={className}
isSelectable={isSelectable} sender={sender}
isSelected={isSelected} isSelectable={isSelectable}
actionIcon={withMediaViewer ? (isMessageDocumentVideo(message) ? 'play' : 'eye') : 'download'} isSelected={isSelected}
onClick={handleClick} actionIcon={withMediaViewer ? (isMessageDocumentVideo(message) ? 'play' : 'eye') : 'download'}
onDateClick={onDateClick ? handleDateClick : undefined} onClick={handleClick}
/> onDateClick={onDateClick ? handleDateClick : undefined}
/>
<ConfirmDialog
isOpen={isSvgDialogOpen}
onClose={closeSvgDialog}
confirmHandler={handleSvgConfirm}
>
{lang('lng_launch_svg_warning')}
<Checkbox
className="dialog-checkbox"
checked={shouldNotWarnAboutSvg}
label={lang('lng_launch_exe_dont_ask')}
onCheck={setShouldNotWarnAboutSvg}
/>
</ConfirmDialog>
</>
); );
}; };

View File

@ -43,6 +43,7 @@ const FileResults: FC<OwnProps & StateProps> = ({
globalMessagesByChatId, globalMessagesByChatId,
foundIds, foundIds,
activeDownloads, activeDownloads,
shouldWarnAboutSvg,
}) => { }) => {
const { const {
searchMessagesGlobal, searchMessagesGlobal,
@ -117,6 +118,7 @@ const FileResults: FC<OwnProps & StateProps> = ({
sender={getSenderName(lang, message, chatsById, usersById)} sender={getSenderName(lang, message, chatsById, usersById)}
className="scroll-item" className="scroll-item"
isDownloading={activeDownloads[message.chatId]?.ids?.includes(message.id)} isDownloading={activeDownloads[message.chatId]?.ids?.includes(message.id)}
shouldWarnAboutSvg={shouldWarnAboutSvg}
observeIntersection={observeIntersectionForMedia} observeIntersection={observeIntersectionForMedia}
onDateClick={handleMessageFocus} onDateClick={handleMessageFocus}
/> />

View File

@ -16,6 +16,7 @@ export type StateProps = {
searchChatId?: string; searchChatId?: string;
activeDownloads: TabState['activeDownloads']['byChatId']; activeDownloads: TabState['activeDownloads']['byChatId'];
isChatProtected?: boolean; isChatProtected?: boolean;
shouldWarnAboutSvg?: boolean;
}; };
export function createMapStateToProps(type: ApiGlobalMessageSearchType) { export function createMapStateToProps(type: ApiGlobalMessageSearchType) {
@ -48,6 +49,7 @@ export function createMapStateToProps(type: ApiGlobalMessageSearchType) {
searchChatId: chatId, searchChatId: chatId,
activeDownloads, activeDownloads,
isChatProtected: chatId ? selectChat(global, chatId)?.isProtected : undefined, isChatProtected: chatId ? selectChat(global, chatId)?.isProtected : undefined,
shouldWarnAboutSvg: global.settings.byKey.shouldWarnAboutSvg,
}; };
}; };
} }

View File

@ -191,8 +191,8 @@ type OwnProps =
appearanceOrder: number; appearanceOrder: number;
isJustAdded: boolean; isJustAdded: boolean;
memoFirstUnreadIdRef: { current: number | undefined }; memoFirstUnreadIdRef: { current: number | undefined };
onPinnedIntersectionChange: PinnedIntersectionChangedCallback;
getIsMessageListReady: Signal<boolean>; getIsMessageListReady: Signal<boolean>;
onPinnedIntersectionChange: PinnedIntersectionChangedCallback;
} }
& MessagePositionProperties; & MessagePositionProperties;
@ -261,6 +261,7 @@ type StateProps = {
withStickerEffects?: boolean; withStickerEffects?: boolean;
webPageStory?: ApiTypeStory; webPageStory?: ApiTypeStory;
isConnected: boolean; isConnected: boolean;
shouldWarnAboutSvg?: boolean;
}; };
type MetaPosition = type MetaPosition =
@ -367,8 +368,9 @@ const Message: FC<OwnProps & StateProps> = ({
withStickerEffects, withStickerEffects,
webPageStory, webPageStory,
isConnected, isConnected,
onPinnedIntersectionChange,
getIsMessageListReady, getIsMessageListReady,
shouldWarnAboutSvg,
onPinnedIntersectionChange,
}) => { }) => {
const { const {
toggleMessageSelection, toggleMessageSelection,
@ -1094,6 +1096,7 @@ const Message: FC<OwnProps & StateProps> = ({
onMediaClick={handleMediaClick} onMediaClick={handleMediaClick}
onCancelUpload={handleCancelUpload} onCancelUpload={handleCancelUpload}
isDownloading={isDownloading} isDownloading={isDownloading}
shouldWarnAboutSvg={shouldWarnAboutSvg}
/> />
)} )}
{storyData && !isStoryMention && ( {storyData && !isStoryMention && (
@ -1584,6 +1587,7 @@ export default memo(withGlobal<OwnProps>(
withStickerEffects: selectPerformanceSettingsValue(global, 'stickerEffects'), withStickerEffects: selectPerformanceSettingsValue(global, 'stickerEffects'),
webPageStory, webPageStory,
isConnected, isConnected,
shouldWarnAboutSvg: global.settings.byKey.shouldWarnAboutSvg,
...((canShowSender || isLocation) && { sender }), ...((canShowSender || isLocation) && { sender }),
...(isOutgoing && { outgoingStatus: selectOutgoingStatus(global, message, messageListType === 'scheduled') }), ...(isOutgoing && { outgoingStatus: selectOutgoingStatus(global, message, messageListType === 'scheduled') }),
...(typeof uploadProgress === 'number' && { uploadProgress }), ...(typeof uploadProgress === 'number' && { uploadProgress }),

View File

@ -112,6 +112,7 @@ type StateProps = {
activeDownloadIds?: number[]; activeDownloadIds?: number[];
isChatProtected?: boolean; isChatProtected?: boolean;
nextProfileTab?: ProfileTabType; nextProfileTab?: ProfileTabType;
shouldWarnAboutSvg?: boolean;
}; };
const TABS = [ const TABS = [
@ -156,6 +157,7 @@ const Profile: FC<OwnProps & StateProps> = ({
activeDownloadIds, activeDownloadIds,
isChatProtected, isChatProtected,
nextProfileTab, nextProfileTab,
shouldWarnAboutSvg,
}) => { }) => {
const { const {
setLocalMediaSearchType, setLocalMediaSearchType,
@ -441,6 +443,7 @@ const Profile: FC<OwnProps & StateProps> = ({
isDownloading={activeDownloadIds?.includes(id)} isDownloading={activeDownloadIds?.includes(id)}
observeIntersection={observeIntersectionForMedia} observeIntersection={observeIntersectionForMedia}
onDateClick={handleMessageFocus} onDateClick={handleMessageFocus}
shouldWarnAboutSvg={shouldWarnAboutSvg}
/> />
)) ))
) : resultType === 'links' ? ( ) : resultType === 'links' ? (
@ -646,6 +649,7 @@ export default memo(withGlobal<OwnProps>(
storyByIds, storyByIds,
isChatProtected: chat?.isProtected, isChatProtected: chat?.isProtected,
nextProfileTab: selectTabState(global).nextProfileTab, nextProfileTab: selectTabState(global).nextProfileTab,
shouldWarnAboutSvg: global.settings.byKey.shouldWarnAboutSvg,
...(hasMembersTab && members && { members, adminMembersById }), ...(hasMembersTab && members && { members, adminMembersById }),
...(hasCommonChatsTab && user && { commonChatIds: user.commonChats?.ids }), ...(hasCommonChatsTab && user && { commonChatIds: user.commonChats?.ids }),
}; };

View File

@ -237,6 +237,7 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
doNotTranslate: [], doNotTranslate: [],
canDisplayChatInTitle: true, canDisplayChatInTitle: true,
shouldAllowHttpTransport: true, shouldAllowHttpTransport: true,
shouldWarnAboutSvg: true,
}, },
themes: { themes: {
light: { light: {

View File

@ -107,6 +107,7 @@ export interface ISettings extends NotifySettings, Record<string, any> {
shouldAllowHttpTransport?: boolean; shouldAllowHttpTransport?: boolean;
shouldCollectDebugLogs?: boolean; shouldCollectDebugLogs?: boolean;
shouldDebugExportedSenders?: boolean; shouldDebugExportedSenders?: boolean;
shouldWarnAboutSvg?: boolean;
} }
export interface ApiPrivacySettings { export interface ApiPrivacySettings {