SVG: Ask before download (#3872)
This commit is contained in:
parent
f8c021b043
commit
bcb1a7ae26
@ -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>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 }),
|
||||||
|
|||||||
@ -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 }),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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: {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user