mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-20 02:08:57 +00:00
Add content protection checkbox to Settings window
This commit is contained in:
@@ -6936,6 +6936,30 @@
|
||||
"messageformat": "Restart",
|
||||
"description": "Button to restart Signal to apply language changes"
|
||||
},
|
||||
"icu:Preferences__Privacy__Application": {
|
||||
"messageformat": "Application",
|
||||
"description": "Title of Application section in Settings > Privacy"
|
||||
},
|
||||
"icu:Preferences__content-protection--label": {
|
||||
"messageformat": "Screen security",
|
||||
"description": "Label of checkbox in Settings > Privacy > Application"
|
||||
},
|
||||
"icu:Preferences__content-protection--description": {
|
||||
"messageformat": "Prevent screenshots of Signal on this computer for added privacy.",
|
||||
"description": "Description of checkbox in Settings > Privacy > Application"
|
||||
},
|
||||
"icu:Preferences__content-protection__modal--title": {
|
||||
"messageformat": "Disable screen security?",
|
||||
"description": "Title of confirm disabling screen security modal in Settings > Privacy > Application"
|
||||
},
|
||||
"icu:Preferences__content-protection__modal--body": {
|
||||
"messageformat": "If disabled, this may allow Microsoft Windows to capture screenshots of Signal and use them for features that may not be private.",
|
||||
"description": "Body of confirm disabling screen security modal in Settings > Privacy > Application"
|
||||
},
|
||||
"icu:Preferences__content-protection__modal--disable": {
|
||||
"messageformat": "Disable",
|
||||
"description": "Text of the button confirm disabling screen security modal in Settings > Privacy > Application"
|
||||
},
|
||||
"icu:DialogUpdate--version-available": {
|
||||
"messageformat": "Update to version {version} available",
|
||||
"description": "Tooltip for new update available"
|
||||
|
||||
24
app/main.ts
24
app/main.ts
@@ -571,6 +571,15 @@ async function handleCommonWindowEvents(window: BrowserWindow) {
|
||||
const focusInterval = setInterval(setWindowFocus, 10000);
|
||||
window.on('closed', () => clearInterval(focusInterval));
|
||||
|
||||
const contentProtection = ephemeralConfig.get('contentProtection');
|
||||
// Apply content protection by default on Windows, unless explicitly disabled
|
||||
// by user in settings.
|
||||
if (contentProtection ?? OS.isWindows()) {
|
||||
window.once('ready-to-show', async () => {
|
||||
window.setContentProtection(true);
|
||||
});
|
||||
}
|
||||
|
||||
await zoomFactorService.syncWindow(window);
|
||||
|
||||
nativeThemeNotifier.addWindow(window);
|
||||
@@ -2132,6 +2141,7 @@ app.on('ready', async () => {
|
||||
'ephemeral-setting-changed',
|
||||
sendPreferencesChangedEventToWindows
|
||||
);
|
||||
settingsChannel.on('ephemeral-setting-changed', onEphemeralSettingChanged);
|
||||
|
||||
// We use this event only a single time to log the startup time of the app
|
||||
// from when it's first ready until the loading screen disappears.
|
||||
@@ -2902,6 +2912,20 @@ const sendPreferencesChangedEventToWindows = () => {
|
||||
};
|
||||
ipc.on('preferences-changed', sendPreferencesChangedEventToWindows);
|
||||
|
||||
const onEphemeralSettingChanged = (name: string) => {
|
||||
if (name !== 'contentProtection') {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentProtection = ephemeralConfig.get('contentProtection');
|
||||
|
||||
for (const window of activeWindows) {
|
||||
if (typeof contentProtection === 'boolean') {
|
||||
window.setContentProtection(contentProtection);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function maybeGetIncomingSignalRoute(argv: Array<string>) {
|
||||
for (const arg of argv) {
|
||||
const route = parseSignalRoute(arg);
|
||||
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
hasAutoLaunch: true,
|
||||
hasCallNotifications: true,
|
||||
hasCallRingtoneNotification: false,
|
||||
hasContentProtection: false,
|
||||
hasCountMutedConversations: false,
|
||||
hasHideMenuBar: false,
|
||||
hasIncomingCallNotifications: true,
|
||||
@@ -136,6 +137,8 @@ export default {
|
||||
isSyncSupported: true,
|
||||
isSystemTraySupported: true,
|
||||
isInternalUser: false,
|
||||
isContentProtectionSupported: true,
|
||||
isContentProtectionNeeded: true,
|
||||
isMinimizeToAndStartInSystemTraySupported: true,
|
||||
lastSyncTime: Date.now(),
|
||||
localeOverride: null,
|
||||
@@ -182,6 +185,7 @@ export default {
|
||||
onCallRingtoneNotificationChange: action(
|
||||
'onCallRingtoneNotificationChange'
|
||||
),
|
||||
onContentProtectionChange: action('onContentProtectionChange'),
|
||||
onCountMutedConversationsChange: action('onCountMutedConversationsChange'),
|
||||
onEmojiSkinToneDefaultChange: action('onEmojiSkinToneDefaultChange'),
|
||||
onHasStoriesDisabledChanged: action('onHasStoriesDisabledChanged'),
|
||||
|
||||
@@ -96,6 +96,7 @@ export type PropsDataType = {
|
||||
hasAutoLaunch: boolean;
|
||||
hasCallNotifications: boolean;
|
||||
hasCallRingtoneNotification: boolean;
|
||||
hasContentProtection: boolean;
|
||||
hasCountMutedConversations: boolean;
|
||||
hasHideMenuBar?: boolean;
|
||||
hasIncomingCallNotifications: boolean;
|
||||
@@ -145,6 +146,8 @@ export type PropsDataType = {
|
||||
isSystemTraySupported: boolean;
|
||||
isMinimizeToAndStartInSystemTraySupported: boolean;
|
||||
isInternalUser: boolean;
|
||||
isContentProtectionNeeded: boolean;
|
||||
isContentProtectionSupported: boolean;
|
||||
|
||||
availableCameras: Array<
|
||||
Pick<MediaDeviceInfo, 'deviceId' | 'groupId' | 'kind' | 'label'>
|
||||
@@ -189,6 +192,7 @@ type PropsFunctionType = {
|
||||
onAutoLaunchChange: CheckboxChangeHandlerType;
|
||||
onCallNotificationsChange: CheckboxChangeHandlerType;
|
||||
onCallRingtoneNotificationChange: CheckboxChangeHandlerType;
|
||||
onContentProtectionChange: CheckboxChangeHandlerType;
|
||||
onCountMutedConversationsChange: CheckboxChangeHandlerType;
|
||||
onEmojiSkinToneDefaultChange: (emojiSkinTone: EmojiSkinTone) => void;
|
||||
onHasStoriesDisabledChanged: SelectChangeHandlerType<boolean>;
|
||||
@@ -297,6 +301,7 @@ export function Preferences({
|
||||
hasAutoLaunch,
|
||||
hasCallNotifications,
|
||||
hasCallRingtoneNotification,
|
||||
hasContentProtection,
|
||||
hasCountMutedConversations,
|
||||
hasHideMenuBar,
|
||||
hasIncomingCallNotifications,
|
||||
@@ -326,6 +331,8 @@ export function Preferences({
|
||||
isSystemTraySupported,
|
||||
isMinimizeToAndStartInSystemTraySupported,
|
||||
isInternalUser,
|
||||
isContentProtectionNeeded,
|
||||
isContentProtectionSupported,
|
||||
lastSyncTime,
|
||||
makeSyncRequest,
|
||||
notificationContent,
|
||||
@@ -336,6 +343,7 @@ export function Preferences({
|
||||
onAutoLaunchChange,
|
||||
onCallNotificationsChange,
|
||||
onCallRingtoneNotificationChange,
|
||||
onContentProtectionChange,
|
||||
onCountMutedConversationsChange,
|
||||
onEmojiSkinToneDefaultChange,
|
||||
onHasStoriesDisabledChanged,
|
||||
@@ -392,6 +400,8 @@ export function Preferences({
|
||||
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
const [confirmStoriesOff, setConfirmStoriesOff] = useState(false);
|
||||
const [confirmContentProtection, setConfirmContentProtection] =
|
||||
useState(false);
|
||||
const [page, setPage] = useState<Page>(initialPage);
|
||||
const [showSyncFailed, setShowSyncFailed] = useState(false);
|
||||
const [nowSyncing, setNowSyncing] = useState(false);
|
||||
@@ -460,6 +470,17 @@ export function Preferences({
|
||||
[onSelectedMicrophoneChange, availableMicrophones]
|
||||
);
|
||||
|
||||
const handleContentProtectionChange = useCallback(
|
||||
(value: boolean) => {
|
||||
if (value === true || !isContentProtectionNeeded) {
|
||||
onContentProtectionChange(value);
|
||||
} else {
|
||||
setConfirmContentProtection(true);
|
||||
}
|
||||
},
|
||||
[onContentProtectionChange, isContentProtectionNeeded]
|
||||
);
|
||||
|
||||
const settingsPaneRef = useRef<HTMLDivElement | null>(null);
|
||||
useEffect(() => {
|
||||
const settingsPane = settingsPaneRef.current;
|
||||
@@ -1365,6 +1386,41 @@ export function Preferences({
|
||||
}
|
||||
/>
|
||||
</SettingsRow>
|
||||
{isContentProtectionSupported && (
|
||||
<SettingsRow title={i18n('icu:Preferences__Privacy__Application')}>
|
||||
<Checkbox
|
||||
checked={hasContentProtection}
|
||||
description={i18n(
|
||||
'icu:Preferences__content-protection--description'
|
||||
)}
|
||||
label={i18n('icu:Preferences__content-protection--label')}
|
||||
moduleClassName="Preferences__checkbox"
|
||||
name="contentProtection"
|
||||
onChange={handleContentProtectionChange}
|
||||
/>
|
||||
</SettingsRow>
|
||||
)}
|
||||
{confirmContentProtection ? (
|
||||
<ConfirmationDialog
|
||||
dialogName="Preference.confirmContentProtection"
|
||||
actions={[
|
||||
{
|
||||
action: () => onContentProtectionChange(false),
|
||||
style: 'negative',
|
||||
text: i18n(
|
||||
'icu:Preferences__content-protection__modal--disable'
|
||||
),
|
||||
},
|
||||
]}
|
||||
i18n={i18n}
|
||||
onClose={() => {
|
||||
setConfirmContentProtection(false);
|
||||
}}
|
||||
title={i18n('icu:Preferences__content-protection__modal--title')}
|
||||
>
|
||||
{i18n('icu:Preferences__content-protection__modal--body')}
|
||||
</ConfirmationDialog>
|
||||
) : null}
|
||||
<SettingsRow title={i18n('icu:Stories__title')}>
|
||||
<Control
|
||||
left={
|
||||
|
||||
@@ -21,6 +21,7 @@ const EPHEMERAL_NAME_MAP = new Map([
|
||||
['systemTraySetting', 'system-tray-setting'],
|
||||
['themeSetting', 'theme-setting'],
|
||||
['localeOverride', 'localeOverride'],
|
||||
['contentProtection', 'contentProtection'],
|
||||
]);
|
||||
|
||||
type ResponseQueueEntry = Readonly<{
|
||||
@@ -123,6 +124,7 @@ export class SettingsChannel extends EventEmitter {
|
||||
this.#installEphemeralSetting('systemTraySetting');
|
||||
this.#installEphemeralSetting('localeOverride');
|
||||
this.#installEphemeralSetting('spellCheck');
|
||||
this.#installEphemeralSetting('contentProtection');
|
||||
|
||||
installPermissionsHandler({ session: session.defaultSession, userConfig });
|
||||
|
||||
@@ -288,7 +290,7 @@ export class SettingsChannel extends EventEmitter {
|
||||
// Notify main to notify windows of preferences change. As for DB-backed
|
||||
// settings, those are set by the renderer, and afterwards the renderer IPC sends
|
||||
// to main the event 'preferences-changed'.
|
||||
this.emit('ephemeral-setting-changed');
|
||||
this.emit('ephemeral-setting-changed', name);
|
||||
|
||||
const mainWindow = this.#mainWindow;
|
||||
if (!mainWindow || !mainWindow.webContents) {
|
||||
@@ -308,7 +310,7 @@ export class SettingsChannel extends EventEmitter {
|
||||
|
||||
public override on(
|
||||
type: 'ephemeral-setting-changed',
|
||||
callback: () => void
|
||||
callback: (name: string) => void
|
||||
): this;
|
||||
|
||||
public override on(
|
||||
@@ -330,7 +332,10 @@ export class SettingsChannel extends EventEmitter {
|
||||
value: string
|
||||
): boolean;
|
||||
|
||||
public override emit(type: 'ephemeral-setting-changed'): boolean;
|
||||
public override emit(
|
||||
type: 'ephemeral-setting-changed',
|
||||
name: string
|
||||
): boolean;
|
||||
|
||||
public override emit(
|
||||
type: SettingChangeEventType<keyof SettingsValuesType>,
|
||||
|
||||
@@ -33,6 +33,12 @@ export const isDrawAttentionSupported = (OS: OSType): boolean => !OS.isMacOS();
|
||||
export const isSystemTraySupported = (OS: OSType): boolean =>
|
||||
OS.isWindows() || OS.isLinux();
|
||||
|
||||
export const isContentProtectionSupported = (OS: OSType): boolean =>
|
||||
OS.isWindows() || OS.isMacOS();
|
||||
|
||||
export const isContentProtectionNeeded = (OS: OSType): boolean =>
|
||||
OS.isWindows();
|
||||
|
||||
export const getDefaultSystemTraySetting = (
|
||||
OS: OSType,
|
||||
appVersion: string
|
||||
|
||||
@@ -192,6 +192,8 @@ type ValuesWithGetters = Omit<
|
||||
| 'mediaPermissions'
|
||||
| 'mediaCameraPermissions'
|
||||
| 'autoLaunch'
|
||||
| 'spellCheck'
|
||||
| 'contentProtection'
|
||||
| 'systemTraySetting'
|
||||
>;
|
||||
|
||||
@@ -240,6 +242,7 @@ export type IPCEventsGettersType = {
|
||||
getZoomFactor: () => Promise<ZoomFactorType>;
|
||||
getLocaleOverride: () => Promise<string | null>;
|
||||
getSpellCheck: () => Promise<boolean>;
|
||||
getContentProtection: () => Promise<boolean>;
|
||||
getSystemTraySetting: () => Promise<SystemTraySetting>;
|
||||
getThemeSetting: () => Promise<ThemeType>;
|
||||
// Events
|
||||
@@ -338,6 +341,12 @@ export function createIPCEvents(
|
||||
account.captureChange('hasStoriesDisabled');
|
||||
window.textsecure.server?.onHasStoriesDisabledChange(value);
|
||||
},
|
||||
getContentProtection: () => {
|
||||
return getEphemeralSetting('contentProtection');
|
||||
},
|
||||
setContentProtection: async (value: boolean) => {
|
||||
await setEphemeralSetting('contentProtection', value);
|
||||
},
|
||||
getStoryViewReceiptsEnabled: () => {
|
||||
return (
|
||||
window.storage.get('storyViewReceiptsEnabled') ??
|
||||
|
||||
@@ -26,6 +26,7 @@ export type ThemeType = 'light' | 'dark' | 'system';
|
||||
|
||||
export type EphemeralSettings = {
|
||||
spellCheck: boolean;
|
||||
contentProtection: boolean;
|
||||
systemTraySetting: SystemTraySetting;
|
||||
themeSetting: ThemeType;
|
||||
localeOverride: string | null;
|
||||
|
||||
@@ -51,6 +51,7 @@ SettingsWindowProps.onRender(
|
||||
hasAutoLaunch,
|
||||
hasCallNotifications,
|
||||
hasCallRingtoneNotification,
|
||||
hasContentProtection,
|
||||
hasCountMutedConversations,
|
||||
hasHideMenuBar,
|
||||
hasIncomingCallNotifications,
|
||||
@@ -77,6 +78,8 @@ SettingsWindowProps.onRender(
|
||||
isNotificationAttentionSupported,
|
||||
isSyncSupported,
|
||||
isSystemTraySupported,
|
||||
isContentProtectionSupported,
|
||||
isContentProtectionNeeded,
|
||||
isInternalUser,
|
||||
lastSyncTime,
|
||||
makeSyncRequest,
|
||||
@@ -88,6 +91,7 @@ SettingsWindowProps.onRender(
|
||||
onAutoLaunchChange,
|
||||
onCallNotificationsChange,
|
||||
onCallRingtoneNotificationChange,
|
||||
onContentProtectionChange,
|
||||
onCountMutedConversationsChange,
|
||||
onEmojiSkinToneDefaultChange,
|
||||
onHasStoriesDisabledChanged,
|
||||
@@ -166,6 +170,7 @@ SettingsWindowProps.onRender(
|
||||
hasAutoLaunch={hasAutoLaunch}
|
||||
hasCallNotifications={hasCallNotifications}
|
||||
hasCallRingtoneNotification={hasCallRingtoneNotification}
|
||||
hasContentProtection={hasContentProtection}
|
||||
hasCountMutedConversations={hasCountMutedConversations}
|
||||
hasHideMenuBar={hasHideMenuBar}
|
||||
hasIncomingCallNotifications={hasIncomingCallNotifications}
|
||||
@@ -195,6 +200,8 @@ SettingsWindowProps.onRender(
|
||||
isNotificationAttentionSupported={isNotificationAttentionSupported}
|
||||
isSyncSupported={isSyncSupported}
|
||||
isSystemTraySupported={isSystemTraySupported}
|
||||
isContentProtectionSupported={isContentProtectionSupported}
|
||||
isContentProtectionNeeded={isContentProtectionNeeded}
|
||||
isInternalUser={isInternalUser}
|
||||
lastSyncTime={lastSyncTime}
|
||||
localeOverride={localeOverride}
|
||||
@@ -207,6 +214,7 @@ SettingsWindowProps.onRender(
|
||||
onAutoLaunchChange={onAutoLaunchChange}
|
||||
onCallNotificationsChange={onCallNotificationsChange}
|
||||
onCallRingtoneNotificationChange={onCallRingtoneNotificationChange}
|
||||
onContentProtectionChange={onContentProtectionChange}
|
||||
onCountMutedConversationsChange={onCountMutedConversationsChange}
|
||||
onEmojiSkinToneDefaultChange={onEmojiSkinToneDefaultChange}
|
||||
onHasStoriesDisabledChanged={onHasStoriesDisabledChanged}
|
||||
|
||||
@@ -48,6 +48,7 @@ const settingNotificationSetting = createSetting('notificationSetting');
|
||||
const settingRelayCalls = createSetting('alwaysRelayCalls');
|
||||
const settingSentMediaQuality = createSetting('sentMediaQualitySetting');
|
||||
const settingSpellCheck = createSetting('spellCheck');
|
||||
const settingContentProtection = createSetting('contentProtection');
|
||||
const settingTextFormatting = createSetting('textFormatting');
|
||||
const settingTheme = createSetting('themeSetting');
|
||||
const settingSystemTraySetting = createSetting('systemTraySetting');
|
||||
@@ -176,6 +177,7 @@ async function renderPreferences() {
|
||||
hasAutoLaunch,
|
||||
hasCallNotifications,
|
||||
hasCallRingtoneNotification,
|
||||
hasContentProtection,
|
||||
hasCountMutedConversations,
|
||||
hasHideMenuBar,
|
||||
hasIncomingCallNotifications,
|
||||
@@ -223,6 +225,7 @@ async function renderPreferences() {
|
||||
hasAutoLaunch: settingAutoLaunch.getValue(),
|
||||
hasCallNotifications: settingCallSystemNotification.getValue(),
|
||||
hasCallRingtoneNotification: settingCallRingtoneNotification.getValue(),
|
||||
hasContentProtection: settingContentProtection.getValue(),
|
||||
hasCountMutedConversations: settingCountMutedConversations.getValue(),
|
||||
hasHideMenuBar: settingHideMenuBar.getValue(),
|
||||
hasIncomingCallNotifications: settingIncomingCallNotification.getValue(),
|
||||
@@ -321,6 +324,7 @@ async function renderPreferences() {
|
||||
hasAutoLaunch,
|
||||
hasCallNotifications,
|
||||
hasCallRingtoneNotification,
|
||||
hasContentProtection,
|
||||
hasCountMutedConversations,
|
||||
hasHideMenuBar,
|
||||
hasIncomingCallNotifications,
|
||||
@@ -385,6 +389,8 @@ async function renderPreferences() {
|
||||
isSyncSupported: !isSyncNotSupported,
|
||||
isInternalUser,
|
||||
isSystemTraySupported: Settings.isSystemTraySupported(OS),
|
||||
isContentProtectionSupported: Settings.isContentProtectionSupported(OS),
|
||||
isContentProtectionNeeded: Settings.isContentProtectionNeeded(OS),
|
||||
isMinimizeToAndStartInSystemTraySupported:
|
||||
Settings.isMinimizeToAndStartInSystemTraySupported(OS),
|
||||
|
||||
@@ -408,6 +414,9 @@ async function renderPreferences() {
|
||||
onCallRingtoneNotificationChange: attachRenderCallback(
|
||||
settingCallRingtoneNotification.setValue
|
||||
),
|
||||
onContentProtectionChange: attachRenderCallback(
|
||||
settingContentProtection.setValue
|
||||
),
|
||||
onCountMutedConversationsChange: attachRenderCallback(
|
||||
settingCountMutedConversations.setValue
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user