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 { useIsIntersecting } from '../../hooks/useIntersectionObserver';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback';
import useMedia from '../../hooks/useMedia';
import useMediaWithLoadProgress from '../../hooks/useMediaWithLoadProgress';
import Checkbox from '../ui/Checkbox';
import ConfirmDialog from '../ui/ConfirmDialog';
import File from './File';
type OwnProps = {
@ -42,12 +45,14 @@ type OwnProps = {
sender?: string;
autoLoadFileMaxSizeMb?: number;
isDownloading?: boolean;
shouldWarnAboutSvg?: boolean;
onCancelUpload?: () => void;
onMediaClick?: () => void;
onDateClick?: (messageId: number, chatId: string) => void;
};
const BYTES_PER_MB = 1024 * 1024;
const SVG_EXTENSIONS = new Set(['svg', 'svgz']);
const Document: FC<OwnProps> = ({
message,
@ -62,16 +67,21 @@ const Document: FC<OwnProps> = ({
sender,
isSelected,
isSelectable,
shouldWarnAboutSvg,
isDownloading,
onCancelUpload,
onMediaClick,
onDateClick,
isDownloading,
}) => {
const dispatch = getActions();
const { cancelMessageMediaDownload, downloadMessageMedia, setSettingOption } = getActions();
// eslint-disable-next-line no-null/no-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 { fileName, size, timestamp } = 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)
);
const handleDownload = useLastCallback(() => {
downloadMessageMedia({ message });
});
const handleClick = useLastCallback(() => {
if (isUploading) {
if (onCancelUpload) {
@ -119,7 +133,7 @@ const Document: FC<OwnProps> = ({
}
if (isDownloading) {
dispatch.cancelMessageMediaDownload({ message });
cancelMessageMediaDownload({ message });
return;
}
@ -130,9 +144,21 @@ const Document: FC<OwnProps> = ({
if (withMediaViewer) {
onMediaClick!();
} else {
dispatch.downloadMessageMedia({ message });
return;
}
if (SVG_EXTENSIONS.has(extension) && shouldWarnAboutSvg) {
openSvgDialog();
return;
}
handleDownload();
});
const handleSvgConfirm = useLastCallback(() => {
setSettingOption({ shouldWarnAboutSvg: !shouldNotWarnAboutSvg });
closeSvgDialog();
handleDownload();
});
const handleDateClick = useLastCallback(() => {
@ -140,26 +166,41 @@ const Document: FC<OwnProps> = ({
});
return (
<File
ref={ref}
name={fileName}
extension={extension}
size={size}
timestamp={withDate ? datetime || timestamp : undefined}
thumbnailDataUri={thumbDataUri}
previewData={localBlobUrl || previewData}
smaller={smaller}
isTransferring={isTransferring}
isUploading={isUploading}
transferProgress={transferProgress}
className={className}
sender={sender}
isSelectable={isSelectable}
isSelected={isSelected}
actionIcon={withMediaViewer ? (isMessageDocumentVideo(message) ? 'play' : 'eye') : 'download'}
onClick={handleClick}
onDateClick={onDateClick ? handleDateClick : undefined}
/>
<>
<File
ref={ref}
name={fileName}
extension={extension}
size={size}
timestamp={withDate ? datetime || timestamp : undefined}
thumbnailDataUri={thumbDataUri}
previewData={localBlobUrl || previewData}
smaller={smaller}
isTransferring={isTransferring}
isUploading={isUploading}
transferProgress={transferProgress}
className={className}
sender={sender}
isSelectable={isSelectable}
isSelected={isSelected}
actionIcon={withMediaViewer ? (isMessageDocumentVideo(message) ? 'play' : 'eye') : 'download'}
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,
foundIds,
activeDownloads,
shouldWarnAboutSvg,
}) => {
const {
searchMessagesGlobal,
@ -117,6 +118,7 @@ const FileResults: FC<OwnProps & StateProps> = ({
sender={getSenderName(lang, message, chatsById, usersById)}
className="scroll-item"
isDownloading={activeDownloads[message.chatId]?.ids?.includes(message.id)}
shouldWarnAboutSvg={shouldWarnAboutSvg}
observeIntersection={observeIntersectionForMedia}
onDateClick={handleMessageFocus}
/>

View File

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

View File

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

View File

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

View File

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

View File

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