Skip to content

Commit bc6edfa

Browse files
committed
feat: add auto start on startup option to user settings
1 parent f3091a2 commit bc6edfa

File tree

10 files changed

+173
-19
lines changed

10 files changed

+173
-19
lines changed

_locales/en/messages.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,11 @@
969969
"supportDescription": "Having issues? Explore help articles or open a ticket with Session Support.",
970970
"supportGoTo": "Go to Support Page",
971971
"systemInformationDesktop": "System Information: {information}",
972+
"settingsCannotChangeDesktop": "Cannot Update Setting",
973+
"settingsStartCategoryDesktop": "Startup",
974+
"launchOnStartDesktop": "Launch on Startup",
975+
"launchOnStartDescriptionDesktop": "Launch Session automatically when your computer starts up.",
976+
"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.",
972977
"tapToRetry": "Tap to retry",
973978
"theContinue": "Continue",
974979
"theDefault": "Default",

preload.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,28 @@ window.getStartInTray = async () => {
146146
});
147147
};
148148

149+
window.setAutoStartEnabled = async autoStart =>
150+
new Promise((resolve, reject) => {
151+
ipc.once('set-auto-start-enabled-response', (_event, error) => {
152+
if (error) {
153+
reject(error);
154+
return;
155+
}
156+
resolve();
157+
return;
158+
});
159+
ipc.send('set-auto-start-enabled', autoStart);
160+
});
161+
162+
window.getAutoStartEnabled = async () => {
163+
return new Promise(resolve => {
164+
ipc.once('get-auto-start-enabled-response', (_event, value) => {
165+
resolve(value);
166+
});
167+
ipc.send('get-auto-start-enabled');
168+
});
169+
};
170+
149171
window.getOpengroupPruning = async () => {
150172
return new Promise(resolve => {
151173
ipc.once('get-opengroup-pruning-response', (_event, value) => {

ts/components/dialog/LocalizedPopupDialog.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,17 @@ export function LocalizedPopupDialog(props: LocalizedPopupDialogState) {
5050
</StyledScrollDescriptionContainer>
5151
<SpacerSM />
5252

53-
<ModalFlexContainer>
54-
<SessionButton
55-
buttonType={SessionButtonType.Simple}
56-
onClick={onClose}
57-
dataTestId="session-confirm-ok-button"
58-
>
59-
{tr('okay')}
60-
</SessionButton>
61-
</ModalFlexContainer>
53+
{!props.hideOkayButton ? (
54+
<ModalFlexContainer>
55+
<SessionButton
56+
buttonType={SessionButtonType.Simple}
57+
onClick={onClose}
58+
dataTestId="session-confirm-ok-button"
59+
>
60+
{tr('okay')}
61+
</SessionButton>
62+
</ModalFlexContainer>
63+
) : null}
6264
<SpacerXS />
6365
</SessionWrapperModal>
6466
);

ts/components/dialog/user-settings/components/SettingsToggleBasic.tsx

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,49 @@
1-
import type { SettingsToggles } from 'react';
1+
import { useCallback, type SettingsToggles } from 'react';
2+
import { useDispatch } from 'react-redux';
23
import { PanelButtonTextWithSubText } from '../../../buttons/panel/PanelButton';
34
import { PanelToggleButton } from '../../../buttons/panel/PanelToggleButton';
45
import { type TrArgs } from '../../../../localization/localeTools';
6+
import { showLocalizedPopupDialog } from '../../LocalizedPopupDialog';
57

6-
export function SettingsToggleBasic({
7-
active,
8-
baseDataTestId,
9-
onClick,
10-
text,
11-
subText,
12-
}: {
8+
type UnavailableProps = {
9+
unavailable: boolean;
10+
modalReasonTitle: TrArgs;
11+
modalReasonDescription: TrArgs;
12+
};
13+
14+
type SettingsToggleBasicProps = {
1315
text: TrArgs;
1416
subText: TrArgs;
1517
baseDataTestId: SettingsToggles;
1618
active: boolean;
19+
unavailableProps?: UnavailableProps;
1720
onClick: () => Promise<void>;
18-
}) {
21+
};
22+
23+
export function SettingsToggleBasic({
24+
text,
25+
subText,
26+
baseDataTestId,
27+
active,
28+
onClick,
29+
unavailableProps,
30+
}: SettingsToggleBasicProps) {
31+
const dispatch = useDispatch();
32+
33+
const handleClick = useCallback(async () => {
34+
if (!unavailableProps?.unavailable) {
35+
return onClick();
36+
}
37+
return showLocalizedPopupDialog(
38+
{
39+
title: unavailableProps?.modalReasonTitle,
40+
description: unavailableProps?.modalReasonDescription,
41+
hideOkayButton: true,
42+
},
43+
dispatch
44+
);
45+
}, [unavailableProps, dispatch, onClick]);
46+
1947
return (
2048
<PanelToggleButton
2149
textElement={
@@ -27,7 +55,7 @@ export function SettingsToggleBasic({
2755
/>
2856
}
2957
active={active}
30-
onClick={onClick}
58+
onClick={handleClick}
3159
toggleDataTestId={`${baseDataTestId}-settings-toggle`}
3260
rowDataTestId={`${baseDataTestId}-settings-row`}
3361
/>

ts/components/dialog/user-settings/pages/PreferencesSettingsPage.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { SettingsKey } from '../../../../data/settings-key';
2222
import { ToastUtils } from '../../../../session/utils';
2323
import { PanelRadioButton } from '../../../buttons/panel/PanelRadioButton';
2424
import { useHasEnterSendEnabled } from '../../../../state/selectors/settings';
25+
import { isLinux } from '../../../../OS';
2526

2627
async function toggleStartInTray() {
2728
try {
@@ -38,6 +39,19 @@ async function toggleStartInTray() {
3839
}
3940
}
4041

42+
async function toggleAutoStart() {
43+
try {
44+
const currentSettings = await window.getAutoStartEnabled();
45+
const newValue = !currentSettings;
46+
47+
// make sure to write it here too, as this is the value used on the UI to mark the toggle as true/false
48+
await window.setSettingValue(SettingsKey.settingsAutoStart, newValue);
49+
await window.setAutoStartEnabled(newValue);
50+
} catch (e) {
51+
window.log.warn('auto start change error:', e);
52+
}
53+
}
54+
4155
function SendWithShiftEnter() {
4256
const initialSetting = useHasEnterSendEnabled();
4357
const selectedWithSettingTrue = 'enterForNewLine';
@@ -100,6 +114,11 @@ export function PreferencesSettingsPage(modalState: UserSettingsModalState) {
100114
const closeAction = useUserSettingsCloseAction(modalState);
101115
const title = useUserSettingsTitle(modalState);
102116
const isStartInTrayActive = Boolean(window.getSettingValue(SettingsKey.settingsStartInTray));
117+
118+
const platformIsLinux = isLinux();
119+
const isAutoStartActive = platformIsLinux
120+
? false
121+
: Boolean(window.getSettingValue(SettingsKey.settingsAutoStart));
103122
const forceUpdate = useUpdate();
104123

105124
return (
@@ -144,6 +163,24 @@ export function PreferencesSettingsPage(modalState: UserSettingsModalState) {
144163
active={isStartInTrayActive}
145164
/>
146165
</PanelButtonGroup>
166+
<PanelLabelWithDescription title={{ token: 'settingsStartCategoryDesktop' }} />
167+
<PanelButtonGroup>
168+
<SettingsToggleBasic
169+
baseDataTestId="auto-start"
170+
text={{ token: 'launchOnStartDesktop' }}
171+
subText={{ token: 'launchOnStartDescriptionDesktop' }}
172+
onClick={async () => {
173+
await toggleAutoStart();
174+
forceUpdate();
175+
}}
176+
active={isAutoStartActive}
177+
unavailableProps={{
178+
unavailable: platformIsLinux,
179+
modalReasonTitle: { token: 'settingsCannotChangeDesktop' },
180+
modalReasonDescription: { token: 'launchOnStartupDisabledDesktop' },
181+
}}
182+
/>
183+
</PanelButtonGroup>
147184
<SendWithShiftEnter />
148185
</SessionWrapperModal>
149186
);

ts/data/settings-key.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const settingsSpellCheck = 'spell-check';
77
const settingsLinkPreview = 'link-preview-setting';
88
const hasBlindedMsgRequestsEnabled = 'hasBlindedMsgRequestsEnabled';
99
const settingsStartInTray = 'start-in-tray-setting';
10+
const settingsAutoStart = 'auto-start-setting';
1011
const settingsOpengroupPruning = 'prune-setting';
1112
const settingsNotification = 'notification-setting';
1213
const settingsAudioNotification = 'audio-notification-setting';
@@ -31,6 +32,7 @@ export const SettingsKey = {
3132
settingsSpellCheck,
3233
settingsLinkPreview,
3334
settingsStartInTray,
35+
settingsAutoStart,
3436
settingsOpengroupPruning,
3537
hasBlindedMsgRequestsEnabled,
3638
settingsNotification,

ts/mains/main_node.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,60 @@ ipc.on('get-start-in-tray', event => {
10231023
}
10241024
});
10251025

1026+
ipc.on('set-auto-start-enabled', (event, newValue) => {
1027+
try {
1028+
userConfig.set('autoStart', newValue);
1029+
1030+
// Set the login item settings based on the platform
1031+
if (process.platform === 'darwin') {
1032+
// macOS
1033+
app.setLoginItemSettings({
1034+
openAtLogin: newValue,
1035+
openAsHidden: false,
1036+
});
1037+
} else if (process.platform === 'win32') {
1038+
// Windows - For Squirrel-based apps, we need to handle the stub launcher - https://www.electronjs.org/docs/latest/api/app/#appsetloginitemsettingssettings-macos-windows
1039+
const appFolder = path.dirname(process.execPath);
1040+
const ourExeName = path.basename(process.execPath);
1041+
const stubLauncher = path.resolve(appFolder, '..', ourExeName);
1042+
1043+
app.setLoginItemSettings({
1044+
openAtLogin: newValue,
1045+
path: stubLauncher,
1046+
args: [],
1047+
});
1048+
} else {
1049+
// Linux and other platforms - basic support
1050+
app.setLoginItemSettings({
1051+
openAtLogin: newValue,
1052+
});
1053+
}
1054+
1055+
event.sender.send('set-auto-start-enabled-response', null);
1056+
} catch (e) {
1057+
event.sender.send('set-auto-start-enabled-response', e);
1058+
}
1059+
});
1060+
1061+
ipc.on('get-auto-start-enabled', event => {
1062+
try {
1063+
const configVal = userConfig.get('autoStart');
1064+
1065+
if (configVal !== undefined) {
1066+
event.sender.send('get-auto-start-enabled-response', configVal);
1067+
return;
1068+
}
1069+
1070+
const loginSettings = app.getLoginItemSettings();
1071+
const isEnabled = loginSettings.openAtLogin;
1072+
1073+
userConfig.set('autoStart', isEnabled);
1074+
event.sender.send('get-auto-start-enabled-response', isEnabled);
1075+
} catch (e) {
1076+
event.sender.send('get-auto-start-enabled-response', false);
1077+
}
1078+
});
1079+
10261080
ipcMain.on('update-badge-count', (_event, count) => {
10271081
if (app.isReady()) {
10281082
app.setBadgeCount(isNumber(count) && isFinite(count) && count >= 0 ? count : 0);

ts/react.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ declare module 'react' {
9595
| 'conversation-trimming'
9696
| 'auto-update'
9797
| 'auto-dark-mode'
98+
| 'auto-start'
9899
| 'hide-menu-bar';
99100

100101
type SettingsRadio =

ts/state/ducks/modalDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export type OpenUrlModalState = { urlToOpen: string } | null;
5555
export type LocalizedPopupDialogState = {
5656
title: TrArgs;
5757
description: TrArgs;
58+
hideOkayButton?: boolean;
5859
} | null;
5960
export type SessionProInfoState = { variant: SessionProInfoVariant } | null;
6061

ts/window.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ declare global {
7474
}) => Promise<void>;
7575
setStartInTray: (val: boolean) => Promise<void>;
7676
getStartInTray: () => Promise<boolean>;
77+
setAutoStartEnabled: (val: boolean) => Promise<void>;
78+
getAutoStartEnabled: () => Promise<boolean>;
7779
getOpengroupPruning: () => Promise<boolean>;
7880
setOpengroupPruning: (val: boolean) => Promise<void>;
7981
closeAbout: () => void;

0 commit comments

Comments
 (0)