Files
fluxer/fluxer_app/src/features/user/components/modals/tabs/DesktopSettingsTab.tsx
T

245 lines
10 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-or-later
import {ConfirmModal} from '@app/features/app/components/dialogs/ConfirmModal';
import {SettingsSection} from '@app/features/app/components/dialogs/shared/SettingsSection';
import {SettingsTabContainer} from '@app/features/app/components/dialogs/shared/SettingsTabLayout';
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import {MacPermissionsSettingsRow} from '@app/features/permissions/system/components/MacPermissionsSettingsRow';
import {
getAutostartStatus,
getCachedAutostartStatus,
setAutostartEnabled,
} from '@app/features/platform/utils/Autostart';
import * as ModalCommands from '@app/features/ui/commands/ModalCommands';
import {modal} from '@app/features/ui/commands/ModalCommands';
import {Switch} from '@app/features/ui/components/form/FormSwitch';
import {
getCachedDesktopWindowBehaviorSettings,
getDesktopWindowBehaviorPendingRestart,
getDesktopWindowBehaviorSettings,
relaunchDesktopApp,
setDesktopWindowBehaviorSettings,
} from '@app/features/ui/utils/DesktopWindowBehaviorUtils';
import {getNativePlatformSync, isDesktop, isNativeLinux, isNativeMacOS} from '@app/features/ui/utils/NativeUtils';
import {
LATER_DESCRIPTOR,
LAUNCH_AT_LOGIN_DESCRIPTOR as LAUNCH_PRODUCT_AT_LOGIN_DESCRIPTOR,
RESTART_DESCRIPTOR,
RESTART_NOW_DESCRIPTOR,
} from '@app/features/user/utils/UserMessageDescriptors';
import type {DesktopWindowBehaviorSettings} from '@app/types/electron.d';
import {msg} from '@lingui/core/macro';
import {Trans, useLingui} from '@lingui/react/macro';
import {observer} from 'mobx-react-lite';
import type React from 'react';
import {useLayoutEffect, useState} from 'react';
const RESTART_PRODUCT_FOR_TRAY_CHANGE_DESCRIPTOR = msg({
message:
"Restart {productName} for this change to take effect because Linux desktops can't reliably add or remove tray icons at runtime.",
comment: 'Desktop setting description shown when a tray-icon change on Linux requires restart.',
});
const USE_COLORED_TRAY_ICON_DESCRIPTOR = msg({
message: 'Use colored tray icon',
comment:
'Desktop setting toggle label on Windows and Linux. Switches the system tray icon from monochrome to the full-color app icon.',
});
const USE_COLORED_TRAY_ICON_DESCRIPTION_DESCRIPTOR = msg({
message: 'Show the full-color {productName} icon instead of a monochrome one that matches your taskbar.',
comment: 'Desktop setting description under the colored tray icon toggle on Windows and Linux.',
});
const DesktopSettingsTab: React.FC = observer(() => {
const {i18n} = useLingui();
const cachedAutostart = getCachedAutostartStatus();
const cachedWindowBehavior = getCachedDesktopWindowBehaviorSettings();
const [autostartEnabled, setAutostartEnabledState] = useState(cachedAutostart ?? false);
const [autostartBusy, setAutostartBusy] = useState(cachedAutostart === null);
const [desktopWindowBehavior, setDesktopWindowBehaviorState] = useState<DesktopWindowBehaviorSettings | null>(
cachedWindowBehavior,
);
const [desktopWindowBehaviorBusy, setDesktopWindowBehaviorBusy] = useState(cachedWindowBehavior === null);
const [trayChangePendingRestart, setTrayChangePendingRestart] = useState(false);
const platform = getNativePlatformSync();
const isMac = isNativeMacOS(platform);
const isLinux = isNativeLinux(platform);
useLayoutEffect(() => {
let mounted = true;
const initDesktopSettings = async () => {
if (!isDesktop()) {
if (mounted) {
setAutostartBusy(false);
setDesktopWindowBehaviorBusy(false);
}
return;
}
const needsAutostart = getCachedAutostartStatus() === null;
const needsWindowBehavior = getCachedDesktopWindowBehaviorSettings() === null;
if (!needsAutostart && !needsWindowBehavior) return;
const [enabled, windowBehavior] = await Promise.all([
needsAutostart ? getAutostartStatus() : Promise.resolve(getCachedAutostartStatus()),
needsWindowBehavior
? getDesktopWindowBehaviorSettings()
: Promise.resolve(getCachedDesktopWindowBehaviorSettings()),
]);
if (!mounted) return;
if (enabled !== null) {
setAutostartEnabledState(enabled);
}
if (windowBehavior !== null) {
setDesktopWindowBehaviorState(windowBehavior);
}
setAutostartBusy(false);
setDesktopWindowBehaviorBusy(false);
};
void initDesktopSettings();
void getDesktopWindowBehaviorPendingRestart().then((pending) => {
if (mounted) setTrayChangePendingRestart(pending);
});
return () => {
mounted = false;
};
}, []);
const handleAutostartChange = async (value: boolean) => {
setAutostartBusy(true);
const nextState = await setAutostartEnabled(value);
if (nextState !== null) {
setAutostartEnabledState(nextState);
}
setAutostartBusy(false);
};
const handleDesktopWindowBehaviorChange = async (settings: Partial<DesktopWindowBehaviorSettings>) => {
setDesktopWindowBehaviorBusy(true);
const nextSettings = await setDesktopWindowBehaviorSettings(settings);
if (nextSettings !== null) {
setDesktopWindowBehaviorState(nextSettings);
}
const pending = await getDesktopWindowBehaviorPendingRestart();
setTrayChangePendingRestart(pending);
setDesktopWindowBehaviorBusy(false);
return {pending};
};
const handleShowTrayIconChange = (value: boolean) => {
void handleDesktopWindowBehaviorChange({showTrayIcon: value}).then(({pending}) => {
if (!pending || !isNativeLinux()) return;
ModalCommands.push(
modal(() => (
<ConfirmModal
title={i18n._(RESTART_DESCRIPTOR, {productName: RuntimeConfig.productName})}
description={
<Trans>
Tray icon changes only apply after a restart on Linux because the system tray protocol doesn't reliably
let apps add or remove icons at runtime. Restart now to apply your change.
</Trans>
}
primaryText={i18n._(RESTART_NOW_DESCRIPTOR)}
primaryVariant="primary"
secondaryText={i18n._(LATER_DESCRIPTOR)}
onPrimary={async () => {
await relaunchDesktopApp();
}}
data-flx="user.desktop-settings-tab.handle-show-tray-icon-change.confirm-modal"
/>
)),
);
});
};
return (
<SettingsTabContainer data-flx="user.desktop-settings-tab.settings-tab-container">
<SettingsSection
id="desktop-window"
title={<Trans>Desktop window</Trans>}
data-flx="user.desktop-settings-tab.settings-tab-section"
>
{isDesktop() && (
<Switch
label={i18n._(LAUNCH_PRODUCT_AT_LOGIN_DESCRIPTOR, {productName: RuntimeConfig.productName})}
value={autostartEnabled}
disabled={autostartBusy}
onChange={handleAutostartChange}
data-flx="user.desktop-settings-tab.switch.autostart-change"
/>
)}
{isDesktop() && desktopWindowBehavior?.startMinimized !== undefined && (
<Switch
label={<Trans>Start minimized</Trans>}
description={
isMac ? (
<Trans>When launched at login, start in the menu bar instead of opening the window</Trans>
) : (
<Trans>When launched at login, start in the system tray instead of opening the window</Trans>
)
}
value={desktopWindowBehavior.startMinimized}
disabled={desktopWindowBehaviorBusy || !autostartEnabled || !desktopWindowBehavior.showTrayIcon}
onChange={(value) => handleDesktopWindowBehaviorChange({startMinimized: value})}
data-flx="user.desktop-settings-tab.switch.start-minimized-change"
/>
)}
{isDesktop() && desktopWindowBehavior && (
<>
<Switch
label={<Trans>Remember window size and position</Trans>}
value={desktopWindowBehavior.rememberWindowState}
disabled={desktopWindowBehaviorBusy}
onChange={(value) => handleDesktopWindowBehaviorChange({rememberWindowState: value})}
data-flx="user.desktop-settings-tab.switch.desktop-window-behavior-change"
/>
<Switch
label={isMac ? <Trans>Show menu bar icon</Trans> : <Trans>Show system tray icon</Trans>}
description={
trayChangePendingRestart && isLinux
? i18n._(RESTART_PRODUCT_FOR_TRAY_CHANGE_DESCRIPTOR, {productName: RuntimeConfig.productName})
: undefined
}
value={desktopWindowBehavior.showTrayIcon}
disabled={desktopWindowBehaviorBusy}
onChange={handleShowTrayIconChange}
data-flx="user.desktop-settings-tab.switch.show-tray-icon-change"
/>
{!isMac && desktopWindowBehavior.coloredTrayIcon !== undefined && (
<Switch
label={i18n._(USE_COLORED_TRAY_ICON_DESCRIPTOR)}
description={i18n._(USE_COLORED_TRAY_ICON_DESCRIPTION_DESCRIPTOR, {
productName: RuntimeConfig.productName,
})}
value={desktopWindowBehavior.coloredTrayIcon}
disabled={desktopWindowBehaviorBusy || !desktopWindowBehavior.showTrayIcon}
onChange={(value) => handleDesktopWindowBehaviorChange({coloredTrayIcon: value})}
data-flx="user.desktop-settings-tab.switch.colored-tray-icon-change"
/>
)}
<Switch
label={isMac ? <Trans>Minimize to menu bar</Trans> : <Trans>Minimize to tray</Trans>}
value={desktopWindowBehavior.minimizeToTray}
disabled={desktopWindowBehaviorBusy || !desktopWindowBehavior.showTrayIcon}
onChange={(value) => handleDesktopWindowBehaviorChange({minimizeToTray: value})}
data-flx="user.desktop-settings-tab.switch.desktop-window-behavior-change--2"
/>
<Switch
label={isMac ? <Trans>Close to menu bar</Trans> : <Trans>Close to tray</Trans>}
value={desktopWindowBehavior.closeToTray}
disabled={desktopWindowBehaviorBusy || !desktopWindowBehavior.showTrayIcon}
onChange={(value) => handleDesktopWindowBehaviorChange({closeToTray: value})}
data-flx="user.desktop-settings-tab.switch.desktop-window-behavior-change--3"
/>
</>
)}
</SettingsSection>
{isMac && (
<SettingsSection
id="macos-permissions"
title={<Trans>macOS permissions</Trans>}
data-flx="user.desktop-settings-tab.macos-permissions-section"
>
<MacPermissionsSettingsRow data-flx="user.desktop-settings-tab.macos-permissions-row" />
</SettingsSection>
)}
</SettingsTabContainer>
);
});
export default DesktopSettingsTab;