Auto start with system
Some checks failed
Queue Release Build / prepare (push) Has been cancelled
Queue Release Build / build-linux (push) Has been cancelled
Queue Release Build / build-windows (push) Has been cancelled
Queue Release Build / finalize (push) Has been cancelled
Deploy Web Apps / deploy (push) Successful in 6m2s
Some checks failed
Queue Release Build / prepare (push) Has been cancelled
Queue Release Build / build-linux (push) Has been cancelled
Queue Release Build / build-windows (push) Has been cancelled
Queue Release Build / finalize (push) Has been cancelled
Deploy Web Apps / deploy (push) Successful in 6m2s
This commit is contained in:
58
electron/app/auto-start.ts
Normal file
58
electron/app/auto-start.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { app } from 'electron';
|
||||||
|
import AutoLaunch from 'auto-launch';
|
||||||
|
import { readDesktopSettings } from '../desktop-settings';
|
||||||
|
|
||||||
|
let autoLauncher: AutoLaunch | null = null;
|
||||||
|
|
||||||
|
function resolveLaunchPath(): string {
|
||||||
|
// AppImage runs from a temporary mount; APPIMAGE points to the real file path.
|
||||||
|
const appImagePath = process.platform === 'linux'
|
||||||
|
? String(process.env['APPIMAGE'] || '').trim()
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return appImagePath || process.execPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAutoLauncher(): AutoLaunch | null {
|
||||||
|
if (!app.isPackaged) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!autoLauncher) {
|
||||||
|
autoLauncher = new AutoLaunch({
|
||||||
|
name: app.getName(),
|
||||||
|
path: resolveLaunchPath()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return autoLauncher;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setAutoStartEnabled(enabled: boolean): Promise<void> {
|
||||||
|
const launcher = getAutoLauncher();
|
||||||
|
|
||||||
|
if (!launcher) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentlyEnabled = await launcher.isEnabled();
|
||||||
|
|
||||||
|
if (currentlyEnabled === enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
await launcher.enable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await launcher.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function synchronizeAutoStartSetting(enabled = readDesktopSettings().autoStart): Promise<void> {
|
||||||
|
try {
|
||||||
|
await setAutoStartEnabled(enabled);
|
||||||
|
} catch {
|
||||||
|
// Auto-launch integration should never block app startup or settings saves.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { app, BrowserWindow } from 'electron';
|
import { app, BrowserWindow } from 'electron';
|
||||||
import { cleanupLinuxScreenShareAudioRouting } from '../audio/linux-screen-share-routing';
|
import { cleanupLinuxScreenShareAudioRouting } from '../audio/linux-screen-share-routing';
|
||||||
import { initializeDesktopUpdater, shutdownDesktopUpdater } from '../update/desktop-updater';
|
import { initializeDesktopUpdater, shutdownDesktopUpdater } from '../update/desktop-updater';
|
||||||
|
import { synchronizeAutoStartSetting } from './auto-start';
|
||||||
import {
|
import {
|
||||||
initializeDatabase,
|
initializeDatabase,
|
||||||
destroyDatabase,
|
destroyDatabase,
|
||||||
@@ -24,6 +25,7 @@ export function registerAppLifecycle(): void {
|
|||||||
setupCqrsHandlers();
|
setupCqrsHandlers();
|
||||||
setupWindowControlHandlers();
|
setupWindowControlHandlers();
|
||||||
setupSystemHandlers();
|
setupSystemHandlers();
|
||||||
|
await synchronizeAutoStartSetting();
|
||||||
initializeDesktopUpdater();
|
initializeDesktopUpdater();
|
||||||
await createWindow();
|
await createWindow();
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export type AutoUpdateMode = 'auto' | 'off' | 'version';
|
|||||||
|
|
||||||
export interface DesktopSettings {
|
export interface DesktopSettings {
|
||||||
autoUpdateMode: AutoUpdateMode;
|
autoUpdateMode: AutoUpdateMode;
|
||||||
|
autoStart: boolean;
|
||||||
hardwareAcceleration: boolean;
|
hardwareAcceleration: boolean;
|
||||||
manifestUrls: string[];
|
manifestUrls: string[];
|
||||||
preferredVersion: string | null;
|
preferredVersion: string | null;
|
||||||
@@ -19,6 +20,7 @@ export interface DesktopSettingsSnapshot extends DesktopSettings {
|
|||||||
|
|
||||||
const DEFAULT_DESKTOP_SETTINGS: DesktopSettings = {
|
const DEFAULT_DESKTOP_SETTINGS: DesktopSettings = {
|
||||||
autoUpdateMode: 'auto',
|
autoUpdateMode: 'auto',
|
||||||
|
autoStart: true,
|
||||||
hardwareAcceleration: true,
|
hardwareAcceleration: true,
|
||||||
manifestUrls: [],
|
manifestUrls: [],
|
||||||
preferredVersion: null,
|
preferredVersion: null,
|
||||||
@@ -81,6 +83,9 @@ export function readDesktopSettings(): DesktopSettings {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
autoUpdateMode: normalizeAutoUpdateMode(parsed.autoUpdateMode),
|
autoUpdateMode: normalizeAutoUpdateMode(parsed.autoUpdateMode),
|
||||||
|
autoStart: typeof parsed.autoStart === 'boolean'
|
||||||
|
? parsed.autoStart
|
||||||
|
: DEFAULT_DESKTOP_SETTINGS.autoStart,
|
||||||
vaapiVideoEncode: typeof parsed.vaapiVideoEncode === 'boolean'
|
vaapiVideoEncode: typeof parsed.vaapiVideoEncode === 'boolean'
|
||||||
? parsed.vaapiVideoEncode
|
? parsed.vaapiVideoEncode
|
||||||
: DEFAULT_DESKTOP_SETTINGS.vaapiVideoEncode,
|
: DEFAULT_DESKTOP_SETTINGS.vaapiVideoEncode,
|
||||||
@@ -102,6 +107,9 @@ export function updateDesktopSettings(patch: Partial<DesktopSettings>): DesktopS
|
|||||||
};
|
};
|
||||||
const nextSettings: DesktopSettings = {
|
const nextSettings: DesktopSettings = {
|
||||||
autoUpdateMode: normalizeAutoUpdateMode(mergedSettings.autoUpdateMode),
|
autoUpdateMode: normalizeAutoUpdateMode(mergedSettings.autoUpdateMode),
|
||||||
|
autoStart: typeof mergedSettings.autoStart === 'boolean'
|
||||||
|
? mergedSettings.autoStart
|
||||||
|
: DEFAULT_DESKTOP_SETTINGS.autoStart,
|
||||||
hardwareAcceleration: typeof mergedSettings.hardwareAcceleration === 'boolean'
|
hardwareAcceleration: typeof mergedSettings.hardwareAcceleration === 'boolean'
|
||||||
? mergedSettings.hardwareAcceleration
|
? mergedSettings.hardwareAcceleration
|
||||||
: DEFAULT_DESKTOP_SETTINGS.hardwareAcceleration,
|
: DEFAULT_DESKTOP_SETTINGS.hardwareAcceleration,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
type DesktopUpdateServerContext
|
type DesktopUpdateServerContext
|
||||||
} from '../update/desktop-updater';
|
} from '../update/desktop-updater';
|
||||||
import { consumePendingDeepLink } from '../app/deep-links';
|
import { consumePendingDeepLink } from '../app/deep-links';
|
||||||
|
import { synchronizeAutoStartSetting } from '../app/auto-start';
|
||||||
|
|
||||||
const DEFAULT_MIME_TYPE = 'application/octet-stream';
|
const DEFAULT_MIME_TYPE = 'application/octet-stream';
|
||||||
const FILE_CLIPBOARD_FORMATS = [
|
const FILE_CLIPBOARD_FORMATS = [
|
||||||
@@ -329,6 +330,7 @@ export function setupSystemHandlers(): void {
|
|||||||
ipcMain.handle('set-desktop-settings', async (_event, patch: Partial<DesktopSettings>) => {
|
ipcMain.handle('set-desktop-settings', async (_event, patch: Partial<DesktopSettings>) => {
|
||||||
const snapshot = updateDesktopSettings(patch);
|
const snapshot = updateDesktopSettings(patch);
|
||||||
|
|
||||||
|
await synchronizeAutoStartSetting(snapshot.autoStart);
|
||||||
await handleDesktopSettingsChanged();
|
await handleDesktopSettingsChanged();
|
||||||
return snapshot;
|
return snapshot;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ export interface ElectronAPI {
|
|||||||
consumePendingDeepLink: () => Promise<string | null>;
|
consumePendingDeepLink: () => Promise<string | null>;
|
||||||
getDesktopSettings: () => Promise<{
|
getDesktopSettings: () => Promise<{
|
||||||
autoUpdateMode: 'auto' | 'off' | 'version';
|
autoUpdateMode: 'auto' | 'off' | 'version';
|
||||||
|
autoStart: boolean;
|
||||||
hardwareAcceleration: boolean;
|
hardwareAcceleration: boolean;
|
||||||
manifestUrls: string[];
|
manifestUrls: string[];
|
||||||
preferredVersion: string | null;
|
preferredVersion: string | null;
|
||||||
@@ -132,12 +133,14 @@ export interface ElectronAPI {
|
|||||||
onAutoUpdateStateChanged: (listener: (state: DesktopUpdateState) => void) => () => void;
|
onAutoUpdateStateChanged: (listener: (state: DesktopUpdateState) => void) => () => void;
|
||||||
setDesktopSettings: (patch: {
|
setDesktopSettings: (patch: {
|
||||||
autoUpdateMode?: 'auto' | 'off' | 'version';
|
autoUpdateMode?: 'auto' | 'off' | 'version';
|
||||||
|
autoStart?: boolean;
|
||||||
hardwareAcceleration?: boolean;
|
hardwareAcceleration?: boolean;
|
||||||
manifestUrls?: string[];
|
manifestUrls?: string[];
|
||||||
preferredVersion?: string | null;
|
preferredVersion?: string | null;
|
||||||
vaapiVideoEncode?: boolean;
|
vaapiVideoEncode?: boolean;
|
||||||
}) => Promise<{
|
}) => Promise<{
|
||||||
autoUpdateMode: 'auto' | 'off' | 'version';
|
autoUpdateMode: 'auto' | 'off' | 'version';
|
||||||
|
autoStart: boolean;
|
||||||
hardwareAcceleration: boolean;
|
hardwareAcceleration: boolean;
|
||||||
manifestUrls: string[];
|
manifestUrls: string[];
|
||||||
preferredVersion: string | null;
|
preferredVersion: string | null;
|
||||||
|
|||||||
50
package-lock.json
generated
50
package-lock.json
generated
@@ -24,6 +24,7 @@
|
|||||||
"@spartan-ng/cli": "^0.0.1-alpha.589",
|
"@spartan-ng/cli": "^0.0.1-alpha.589",
|
||||||
"@spartan-ng/ui-core": "^0.0.1-alpha.380",
|
"@spartan-ng/ui-core": "^0.0.1-alpha.380",
|
||||||
"@timephy/rnnoise-wasm": "^1.0.0",
|
"@timephy/rnnoise-wasm": "^1.0.0",
|
||||||
|
"auto-launch": "^5.0.6",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cytoscape": "^3.33.1",
|
"cytoscape": "^3.33.1",
|
||||||
@@ -45,11 +46,12 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/build": "^21.0.4",
|
"@angular/build": "^21.0.4",
|
||||||
"@angular/cli": "^21.2.1",
|
"@angular/cli": "^21.0.4",
|
||||||
"@angular/compiler-cli": "^21.0.0",
|
"@angular/compiler-cli": "^21.0.0",
|
||||||
"@eslint/js": "^9.39.3",
|
"@eslint/js": "^9.39.3",
|
||||||
"@stylistic/eslint-plugin-js": "^4.4.1",
|
"@stylistic/eslint-plugin-js": "^4.4.1",
|
||||||
"@stylistic/eslint-plugin-ts": "^4.4.1",
|
"@stylistic/eslint-plugin-ts": "^4.4.1",
|
||||||
|
"@types/auto-launch": "^5.0.5",
|
||||||
"@types/simple-peer": "^9.11.9",
|
"@types/simple-peer": "^9.11.9",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"angular-eslint": "21.2.0",
|
"angular-eslint": "21.2.0",
|
||||||
@@ -10816,6 +10818,13 @@
|
|||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/auto-launch": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/auto-launch/-/auto-launch-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-/nGvQZSzM/pvCMCh4Gt2kIeiUmOP/cKGJbjlInI+A+5MoV/7XmT56DJ6EU8bqc3+ItxEe4UC2GVspmPzcCc8cg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/body-parser": {
|
"node_modules/@types/body-parser": {
|
||||||
"version": "1.19.6",
|
"version": "1.19.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
|
||||||
@@ -12875,6 +12884,11 @@
|
|||||||
"node": ">= 6.0.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/applescript": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/applescript/-/applescript-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-yvtNHdWvtbYEiIazXAdp/NY+BBb65/DAseqlNiJQjOx9DynuzOYDbVLBJvuc0ve0VL9x6B3OHF6eH52y9hCBtQ=="
|
||||||
|
},
|
||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
@@ -12968,6 +12982,22 @@
|
|||||||
"node": ">= 4.0.0"
|
"node": ">= 4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/auto-launch": {
|
||||||
|
"version": "5.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/auto-launch/-/auto-launch-5.0.6.tgz",
|
||||||
|
"integrity": "sha512-OgxiAm4q9EBf9EeXdPBiVNENaWE3jUZofwrhAkWjHDYGezu1k3FRZHU8V2FBxGuSJOHzKmTJEd0G7L7/0xDGFA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"applescript": "^1.0.0",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"path-is-absolute": "^1.0.0",
|
||||||
|
"untildify": "^3.0.2",
|
||||||
|
"winreg": "1.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.23",
|
"version": "10.4.23",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
|
||||||
@@ -22285,9 +22315,7 @@
|
|||||||
"version": "0.5.6",
|
"version": "0.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
},
|
},
|
||||||
@@ -23745,7 +23773,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -29571,6 +29598,15 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/untildify": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/upath": {
|
"node_modules/upath": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz",
|
||||||
@@ -31161,6 +31197,12 @@
|
|||||||
"integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
|
"integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/winreg": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-IHpzORub7kYlb8A43Iig3reOvlcBJGX9gZ0WycHhghHtA65X0LYnMRuJs+aH1abVnMJztQkvQNlltnbPi5aGIA==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
"node_modules/word-wrap": {
|
"node_modules/word-wrap": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
"@spartan-ng/cli": "^0.0.1-alpha.589",
|
"@spartan-ng/cli": "^0.0.1-alpha.589",
|
||||||
"@spartan-ng/ui-core": "^0.0.1-alpha.380",
|
"@spartan-ng/ui-core": "^0.0.1-alpha.380",
|
||||||
"@timephy/rnnoise-wasm": "^1.0.0",
|
"@timephy/rnnoise-wasm": "^1.0.0",
|
||||||
|
"auto-launch": "^5.0.6",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cytoscape": "^3.33.1",
|
"cytoscape": "^3.33.1",
|
||||||
@@ -96,6 +97,7 @@
|
|||||||
"@eslint/js": "^9.39.3",
|
"@eslint/js": "^9.39.3",
|
||||||
"@stylistic/eslint-plugin-js": "^4.4.1",
|
"@stylistic/eslint-plugin-js": "^4.4.1",
|
||||||
"@stylistic/eslint-plugin-ts": "^4.4.1",
|
"@stylistic/eslint-plugin-ts": "^4.4.1",
|
||||||
|
"@types/auto-launch": "^5.0.5",
|
||||||
"@types/simple-peer": "^9.11.9",
|
"@types/simple-peer": "^9.11.9",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"angular-eslint": "21.2.0",
|
"angular-eslint": "21.2.0",
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Injectable, signal } from '@angular/core';
|
import { Injectable, signal } from '@angular/core';
|
||||||
export type SettingsPage = 'network' | 'voice' | 'updates' | 'debugging' | 'server' | 'members' | 'bans' | 'permissions';
|
export type SettingsPage = 'general' | 'network' | 'voice' | 'updates' | 'debugging' | 'server' | 'members' | 'bans' | 'permissions';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class SettingsModalService {
|
export class SettingsModalService {
|
||||||
readonly isOpen = signal(false);
|
readonly isOpen = signal(false);
|
||||||
readonly activePage = signal<SettingsPage>('network');
|
readonly activePage = signal<SettingsPage>('general');
|
||||||
readonly targetServerId = signal<string | null>(null);
|
readonly targetServerId = signal<string | null>(null);
|
||||||
|
|
||||||
open(page: SettingsPage = 'network', serverId?: string): void {
|
open(page: SettingsPage = 'general', serverId?: string): void {
|
||||||
this.activePage.set(page);
|
this.activePage.set(page);
|
||||||
this.targetServerId.set(serverId ?? null);
|
this.targetServerId.set(serverId ?? null);
|
||||||
this.isOpen.set(true);
|
this.isOpen.set(true);
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<div class="space-y-6 max-w-xl">
|
||||||
|
<section>
|
||||||
|
<div class="flex items-center gap-2 mb-3">
|
||||||
|
<ng-icon
|
||||||
|
name="lucidePower"
|
||||||
|
class="w-5 h-5 text-muted-foreground"
|
||||||
|
/>
|
||||||
|
<h4 class="text-sm font-semibold text-foreground">Application</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="rounded-lg border border-border bg-secondary/20 p-4 transition-opacity"
|
||||||
|
[class.opacity-60]="!isElectron"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-medium text-foreground">Launch on system startup</p>
|
||||||
|
|
||||||
|
@if (isElectron) {
|
||||||
|
<p class="text-xs text-muted-foreground">Automatically start MetoYou when you sign in</p>
|
||||||
|
} @else {
|
||||||
|
<p class="text-xs text-muted-foreground">This setting is only available in the desktop app.</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label
|
||||||
|
class="relative inline-flex items-center"
|
||||||
|
[class.cursor-pointer]="isElectron && !savingAutoStart()"
|
||||||
|
[class.cursor-not-allowed]="!isElectron || savingAutoStart()"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
[checked]="autoStart()"
|
||||||
|
[disabled]="!isElectron || savingAutoStart()"
|
||||||
|
(change)="onAutoStartChange($event)"
|
||||||
|
id="general-auto-start-toggle"
|
||||||
|
aria-label="Toggle launch on startup"
|
||||||
|
class="sr-only peer"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="w-10 h-5 bg-secondary rounded-full peer peer-checked:bg-primary peer-disabled:bg-muted/80 peer-disabled:after:bg-muted-foreground/40 peer-checked:after:translate-x-full after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all"
|
||||||
|
></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/member-ordering */
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
inject,
|
||||||
|
signal
|
||||||
|
} from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||||
|
import { lucidePower } from '@ng-icons/lucide';
|
||||||
|
|
||||||
|
import { PlatformService } from '../../../../core/services/platform.service';
|
||||||
|
|
||||||
|
interface DesktopSettingsSnapshot {
|
||||||
|
autoStart: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GeneralSettingsElectronApi {
|
||||||
|
getDesktopSettings?: () => Promise<DesktopSettingsSnapshot>;
|
||||||
|
setDesktopSettings?: (patch: { autoStart?: boolean }) => Promise<DesktopSettingsSnapshot>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeneralSettingsWindow = Window & {
|
||||||
|
electronAPI?: GeneralSettingsElectronApi;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-general-settings',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, NgIcon],
|
||||||
|
viewProviders: [
|
||||||
|
provideIcons({
|
||||||
|
lucidePower
|
||||||
|
})
|
||||||
|
],
|
||||||
|
templateUrl: './general-settings.component.html'
|
||||||
|
})
|
||||||
|
export class GeneralSettingsComponent {
|
||||||
|
private platform = inject(PlatformService);
|
||||||
|
|
||||||
|
readonly isElectron = this.platform.isElectron;
|
||||||
|
autoStart = signal(false);
|
||||||
|
savingAutoStart = signal(false);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
if (this.isElectron) {
|
||||||
|
void this.loadDesktopSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onAutoStartChange(event: Event): Promise<void> {
|
||||||
|
const input = event.target as HTMLInputElement;
|
||||||
|
const enabled = !!input.checked;
|
||||||
|
const api = this.getElectronApi();
|
||||||
|
|
||||||
|
if (!this.isElectron || !api?.setDesktopSettings) {
|
||||||
|
input.checked = this.autoStart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.savingAutoStart.set(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const snapshot = await api.setDesktopSettings({ autoStart: enabled });
|
||||||
|
|
||||||
|
this.autoStart.set(snapshot.autoStart);
|
||||||
|
} catch {
|
||||||
|
input.checked = this.autoStart();
|
||||||
|
} finally {
|
||||||
|
this.savingAutoStart.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadDesktopSettings(): Promise<void> {
|
||||||
|
const api = this.getElectronApi();
|
||||||
|
|
||||||
|
if (!api?.getDesktopSettings) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const snapshot = await api.getDesktopSettings();
|
||||||
|
|
||||||
|
this.autoStart.set(snapshot.autoStart);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getElectronApi(): GeneralSettingsElectronApi | null {
|
||||||
|
return typeof window !== 'undefined'
|
||||||
|
? (window as GeneralSettingsWindow).electronAPI ?? null
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -116,6 +116,9 @@
|
|||||||
<div class="flex items-center justify-between px-6 py-4 border-b border-border flex-shrink-0">
|
<div class="flex items-center justify-between px-6 py-4 border-b border-border flex-shrink-0">
|
||||||
<h3 class="text-lg font-semibold text-foreground">
|
<h3 class="text-lg font-semibold text-foreground">
|
||||||
@switch (activePage()) {
|
@switch (activePage()) {
|
||||||
|
@case ('general') {
|
||||||
|
General
|
||||||
|
}
|
||||||
@case ('network') {
|
@case ('network') {
|
||||||
Network
|
Network
|
||||||
}
|
}
|
||||||
@@ -157,6 +160,9 @@
|
|||||||
<!-- Scrollable Content Area -->
|
<!-- Scrollable Content Area -->
|
||||||
<div class="flex-1 overflow-y-auto p-6">
|
<div class="flex-1 overflow-y-auto p-6">
|
||||||
@switch (activePage()) {
|
@switch (activePage()) {
|
||||||
|
@case ('general') {
|
||||||
|
<app-general-settings />
|
||||||
|
}
|
||||||
@case ('network') {
|
@case ('network') {
|
||||||
<app-network-settings />
|
<app-network-settings />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { Room, UserRole } from '../../../core/models/index';
|
|||||||
import { findRoomMember } from '../../../store/rooms/room-members.helpers';
|
import { findRoomMember } from '../../../store/rooms/room-members.helpers';
|
||||||
import { WebRTCService } from '../../../core/services/webrtc.service';
|
import { WebRTCService } from '../../../core/services/webrtc.service';
|
||||||
|
|
||||||
|
import { GeneralSettingsComponent } from './general-settings/general-settings.component';
|
||||||
import { NetworkSettingsComponent } from './network-settings/network-settings.component';
|
import { NetworkSettingsComponent } from './network-settings/network-settings.component';
|
||||||
import { VoiceSettingsComponent } from './voice-settings/voice-settings.component';
|
import { VoiceSettingsComponent } from './voice-settings/voice-settings.component';
|
||||||
import { ServerSettingsComponent } from './server-settings/server-settings.component';
|
import { ServerSettingsComponent } from './server-settings/server-settings.component';
|
||||||
@@ -48,6 +49,7 @@ import { THIRD_PARTY_LICENSES, type ThirdPartyLicense } from './third-party-lice
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NgIcon,
|
NgIcon,
|
||||||
|
GeneralSettingsComponent,
|
||||||
NetworkSettingsComponent,
|
NetworkSettingsComponent,
|
||||||
VoiceSettingsComponent,
|
VoiceSettingsComponent,
|
||||||
UpdatesSettingsComponent,
|
UpdatesSettingsComponent,
|
||||||
@@ -89,6 +91,9 @@ export class SettingsModalComponent {
|
|||||||
activePage = this.modal.activePage;
|
activePage = this.modal.activePage;
|
||||||
|
|
||||||
readonly globalPages: { id: SettingsPage; label: string; icon: string }[] = [
|
readonly globalPages: { id: SettingsPage; label: string; icon: string }[] = [
|
||||||
|
{ id: 'general',
|
||||||
|
label: 'General',
|
||||||
|
icon: 'lucideSettings' },
|
||||||
{ id: 'network',
|
{ id: 'network',
|
||||||
label: 'Network',
|
label: 'Network',
|
||||||
icon: 'lucideGlobe' },
|
icon: 'lucideGlobe' },
|
||||||
|
|||||||
Reference in New Issue
Block a user