diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 3b305e3f7..951ed9701 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -969,6 +969,11 @@
"supportDescription": "Having issues? Explore help articles or open a ticket with Session Support.",
"supportGoTo": "Go to Support Page",
"systemInformationDesktop": "System Information: {information}",
+ "settingsCannotChangeDesktop": "Cannot Update Setting",
+ "settingsStartCategoryDesktop": "Startup",
+ "launchOnStartDesktop": "Launch on Startup",
+ "launchOnStartDescriptionDesktop": "Launch Session automatically when your computer starts up.",
+ "launchOnStartupDisabledDesktop": "This setting is managed by your system on Linux. To have Session start automatically, please add it to your startup applications in your system settings.",
"tapToRetry": "Tap to retry",
"theContinue": "Continue",
"theDefault": "Default",
diff --git a/preload.js b/preload.js
index 68a925d9c..695f09cf4 100644
--- a/preload.js
+++ b/preload.js
@@ -12,6 +12,7 @@ const { isEmpty } = require('lodash');
const { setupI18n } = require('./ts/util/i18n/i18n');
const { UserUtils } = require('./ts/session/utils');
const { BlindingActions } = require('./ts/webworker/workers/browser/libsession_worker_interface');
+const { SettingsKey } = require('./ts/data/settings-key');
const { crowdinLocale } = ipc.sendSync('locale-data');
@@ -135,6 +136,7 @@ window.setStartInTray = async startInTray =>
return;
});
ipc.send('start-in-tray-on-start', startInTray);
+ void window.setSettingValue(SettingsKey.settingsStartInTray, startInTray);
});
window.getStartInTray = async () => {
@@ -146,6 +148,29 @@ window.getStartInTray = async () => {
});
};
+window.setAutoStartEnabled = async autoStart =>
+ new Promise((resolve, reject) => {
+ ipc.once('set-auto-start-enabled-response', (_event, error) => {
+ if (error) {
+ reject(error);
+ return;
+ }
+ resolve();
+ return;
+ });
+ ipc.send('set-auto-start-enabled', autoStart);
+ void window.setSettingValue(SettingsKey.settingsAutoStart, autoStart);
+ });
+
+window.getAutoStartEnabled = async () => {
+ return new Promise(resolve => {
+ ipc.once('get-auto-start-enabled-response', (_event, value) => {
+ resolve(value);
+ });
+ ipc.send('get-auto-start-enabled');
+ });
+};
+
window.getOpengroupPruning = async () => {
return new Promise(resolve => {
ipc.once('get-opengroup-pruning-response', (_event, value) => {
diff --git a/ts/components/dialog/LocalizedPopupDialog.tsx b/ts/components/dialog/LocalizedPopupDialog.tsx
index f6bd58a46..65ca2794e 100644
--- a/ts/components/dialog/LocalizedPopupDialog.tsx
+++ b/ts/components/dialog/LocalizedPopupDialog.tsx
@@ -50,15 +50,17 @@ export function LocalizedPopupDialog(props: LocalizedPopupDialogState) {
-
-
- {tr('okay')}
-
-
+ {!props.hideOkayButton ? (
+
+
+ {tr('okay')}
+
+
+ ) : null}
);
diff --git a/ts/components/dialog/user-settings/components/SettingsToggleBasic.tsx b/ts/components/dialog/user-settings/components/SettingsToggleBasic.tsx
index 6540e642e..04031f01f 100644
--- a/ts/components/dialog/user-settings/components/SettingsToggleBasic.tsx
+++ b/ts/components/dialog/user-settings/components/SettingsToggleBasic.tsx
@@ -1,21 +1,49 @@
-import type { SettingsToggles } from 'react';
+import { useCallback, type SettingsToggles } from 'react';
+import { useDispatch } from 'react-redux';
import { PanelButtonTextWithSubText } from '../../../buttons/panel/PanelButton';
import { PanelToggleButton } from '../../../buttons/panel/PanelToggleButton';
import { type TrArgs } from '../../../../localization/localeTools';
+import { showLocalizedPopupDialog } from '../../LocalizedPopupDialog';
-export function SettingsToggleBasic({
- active,
- baseDataTestId,
- onClick,
- text,
- subText,
-}: {
+type UnavailableProps = {
+ unavailable: boolean;
+ modalReasonTitle: TrArgs;
+ modalReasonDescription: TrArgs;
+};
+
+type SettingsToggleBasicProps = {
text: TrArgs;
subText: TrArgs;
baseDataTestId: SettingsToggles;
active: boolean;
+ unavailableProps?: UnavailableProps;
onClick: () => Promise;
-}) {
+};
+
+export function SettingsToggleBasic({
+ text,
+ subText,
+ baseDataTestId,
+ active,
+ onClick,
+ unavailableProps,
+}: SettingsToggleBasicProps) {
+ const dispatch = useDispatch();
+
+ const handleClick = useCallback(async () => {
+ if (!unavailableProps?.unavailable) {
+ return onClick();
+ }
+ return showLocalizedPopupDialog(
+ {
+ title: unavailableProps?.modalReasonTitle,
+ description: unavailableProps?.modalReasonDescription,
+ hideOkayButton: true,
+ },
+ dispatch
+ );
+ }, [unavailableProps, dispatch, onClick]);
+
return (
}
active={active}
- onClick={onClick}
+ onClick={handleClick}
toggleDataTestId={`${baseDataTestId}-settings-toggle`}
rowDataTestId={`${baseDataTestId}-settings-row`}
/>
diff --git a/ts/components/dialog/user-settings/pages/PreferencesSettingsPage.tsx b/ts/components/dialog/user-settings/pages/PreferencesSettingsPage.tsx
index 6571f4e4c..10e5c6333 100644
--- a/ts/components/dialog/user-settings/pages/PreferencesSettingsPage.tsx
+++ b/ts/components/dialog/user-settings/pages/PreferencesSettingsPage.tsx
@@ -22,13 +22,12 @@ import { SettingsKey } from '../../../../data/settings-key';
import { ToastUtils } from '../../../../session/utils';
import { PanelRadioButton } from '../../../buttons/panel/PanelRadioButton';
import { useHasEnterSendEnabled } from '../../../../state/selectors/settings';
+import { isLinux } from '../../../../OS';
async function toggleStartInTray() {
try {
const newValue = !(await window.getStartInTray());
- // make sure to write it here too, as this is the value used on the UI to mark the toggle as true/false
- await window.setSettingValue(SettingsKey.settingsStartInTray, newValue);
await window.setStartInTray(newValue);
if (!newValue) {
ToastUtils.pushRestartNeeded();
@@ -38,6 +37,16 @@ async function toggleStartInTray() {
}
}
+async function toggleAutoStart() {
+ try {
+ const newValue = !(await window.getAutoStartEnabled());
+
+ await window.setAutoStartEnabled(newValue);
+ } catch (e) {
+ window.log.warn('auto start change error:', e);
+ }
+}
+
function SendWithShiftEnter() {
const initialSetting = useHasEnterSendEnabled();
const selectedWithSettingTrue = 'enterForNewLine';
@@ -100,6 +109,11 @@ export function PreferencesSettingsPage(modalState: UserSettingsModalState) {
const closeAction = useUserSettingsCloseAction(modalState);
const title = useUserSettingsTitle(modalState);
const isStartInTrayActive = Boolean(window.getSettingValue(SettingsKey.settingsStartInTray));
+
+ const platformIsLinux = isLinux();
+ const isAutoStartActive = platformIsLinux
+ ? false
+ : Boolean(window.getSettingValue(SettingsKey.settingsAutoStart));
const forceUpdate = useUpdate();
return (
@@ -144,6 +158,24 @@ export function PreferencesSettingsPage(modalState: UserSettingsModalState) {
active={isStartInTrayActive}
/>
+
+
+ {
+ await toggleAutoStart();
+ forceUpdate();
+ }}
+ active={isAutoStartActive}
+ unavailableProps={{
+ unavailable: platformIsLinux,
+ modalReasonTitle: { token: 'settingsCannotChangeDesktop' },
+ modalReasonDescription: { token: 'launchOnStartupDisabledDesktop' },
+ }}
+ />
+
);
diff --git a/ts/data/settings-key.ts b/ts/data/settings-key.ts
index 0f1656d96..dbaa4234c 100644
--- a/ts/data/settings-key.ts
+++ b/ts/data/settings-key.ts
@@ -7,6 +7,7 @@ const settingsSpellCheck = 'spell-check';
const settingsLinkPreview = 'link-preview-setting';
const hasBlindedMsgRequestsEnabled = 'hasBlindedMsgRequestsEnabled';
const settingsStartInTray = 'start-in-tray-setting';
+const settingsAutoStart = 'auto-start-setting';
const settingsOpengroupPruning = 'prune-setting';
const settingsNotification = 'notification-setting';
const settingsAudioNotification = 'audio-notification-setting';
@@ -31,6 +32,7 @@ export const SettingsKey = {
settingsSpellCheck,
settingsLinkPreview,
settingsStartInTray,
+ settingsAutoStart,
settingsOpengroupPruning,
hasBlindedMsgRequestsEnabled,
settingsNotification,
diff --git a/ts/mains/main_node.ts b/ts/mains/main_node.ts
index df0ee1ab9..384036f3e 100644
--- a/ts/mains/main_node.ts
+++ b/ts/mains/main_node.ts
@@ -177,6 +177,7 @@ import { initializeMainProcessLogger } from '../util/logger/main_process_logging
import * as log from '../util/logger/log';
import { DURATION } from '../session/constants';
import { tr } from '../localization/localeTools';
+import { isMacOS, isWindows } from '../OS';
function prepareURL(pathSegments: Array, moreKeys?: { theme: any }) {
const urlObject: url.UrlObject = {
@@ -1023,6 +1024,46 @@ ipc.on('get-start-in-tray', event => {
}
});
+ipc.on('set-auto-start-enabled', (event, newValue) => {
+ try {
+ // Set the login item settings based on the platform
+ if (isMacOS()) {
+ // macOS
+ app.setLoginItemSettings({
+ openAtLogin: newValue,
+ openAsHidden: false,
+ });
+ } else if (isWindows()) {
+ // Windows - For Squirrel-based apps, we need to handle the stub launcher - https://www.electronjs.org/docs/latest/api/app/#appsetloginitemsettingssettings-macos-windows
+ const appFolder = path.dirname(process.execPath);
+ const ourExeName = path.basename(process.execPath);
+ const stubLauncher = path.resolve(appFolder, '..', ourExeName);
+
+ app.setLoginItemSettings({
+ openAtLogin: newValue,
+ path: stubLauncher,
+ args: [],
+ });
+ } else {
+ throw new Error(`Unsupported platform for auto start: ${process.platform}`);
+ }
+
+ event.sender.send('set-auto-start-enabled-response', null);
+ } catch (e) {
+ event.sender.send('set-auto-start-enabled-response', e);
+ }
+});
+
+ipc.on('get-auto-start-enabled', event => {
+ try {
+ const loginSettings = app.getLoginItemSettings();
+ const isEnabled = loginSettings.openAtLogin;
+ event.sender.send('get-auto-start-enabled-response', isEnabled);
+ } catch (e) {
+ event.sender.send('get-auto-start-enabled-response', false);
+ }
+});
+
ipcMain.on('update-badge-count', (_event, count) => {
if (app.isReady()) {
app.setBadgeCount(isNumber(count) && isFinite(count) && count >= 0 ? count : 0);
diff --git a/ts/react.d.ts b/ts/react.d.ts
index 9559888a6..65992f18f 100644
--- a/ts/react.d.ts
+++ b/ts/react.d.ts
@@ -95,6 +95,7 @@ declare module 'react' {
| 'conversation-trimming'
| 'auto-update'
| 'auto-dark-mode'
+ | 'auto-start'
| 'hide-menu-bar';
type SettingsRadio =
diff --git a/ts/state/ducks/modalDialog.tsx b/ts/state/ducks/modalDialog.tsx
index 92e33f1d1..a64103693 100644
--- a/ts/state/ducks/modalDialog.tsx
+++ b/ts/state/ducks/modalDialog.tsx
@@ -55,6 +55,7 @@ export type OpenUrlModalState = { urlToOpen: string } | null;
export type LocalizedPopupDialogState = {
title: TrArgs;
description: TrArgs;
+ hideOkayButton?: boolean;
} | null;
export type SessionProInfoState = { variant: SessionProInfoVariant } | null;
diff --git a/ts/window.d.ts b/ts/window.d.ts
index 883aeb817..0f7264ea9 100644
--- a/ts/window.d.ts
+++ b/ts/window.d.ts
@@ -74,6 +74,8 @@ declare global {
}) => Promise;
setStartInTray: (val: boolean) => Promise;
getStartInTray: () => Promise;
+ setAutoStartEnabled: (val: boolean) => Promise;
+ getAutoStartEnabled: () => Promise;
getOpengroupPruning: () => Promise;
setOpengroupPruning: (val: boolean) => Promise;
closeAbout: () => void;