mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
feat(electron): suffix version string per distribution target
Append a short channel marker to the (display-only) version string so bug reports and the config footer reveal which build a user runs, e.g.: win nsis W portable P MS Store MS mac dmg D App Store MAS linux AppImage AI snap SN flatpak FP deb/rpm L android Play A F-Droid AF ios I web WB Desktop channels are detected in the Electron process (process.platform, process.mas/windowsStore, APPIMAGE/SNAP/FLATPAK_ID) via a shared helper reused by the tray-GUID logic and exposed sync over the preload bridge (no IPC, getAppVersionStr stays synchronous). Mobile/web are detected in the frontend. deb vs rpm is not distinguishable at runtime, so both report L. No caller semver-compares this string.
This commit is contained in:
parent
e58dc04179
commit
79133f9046
6 changed files with 184 additions and 7 deletions
5
electron/electronAPI.d.ts
vendored
5
electron/electronAPI.d.ts
vendored
|
|
@ -19,6 +19,7 @@ import {
|
|||
LocalRestApiRequestPayload,
|
||||
LocalRestApiResponsePayload,
|
||||
} from './shared-with-frontend/local-rest-api.model';
|
||||
import { ElectronDistChannel } from './shared-with-frontend/get-dist-channel';
|
||||
|
||||
export interface ElectronAPI {
|
||||
on(
|
||||
|
|
@ -26,6 +27,10 @@ export interface ElectronAPI {
|
|||
listener: (event: IpcRendererEvent, ...args: unknown[]) => void,
|
||||
): void;
|
||||
|
||||
// SYNC
|
||||
// ----
|
||||
getDistChannel(): ElectronDistChannel | null;
|
||||
|
||||
// INVOKE
|
||||
// ------
|
||||
getUserDataPath(): Promise<string>;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
} from 'electron';
|
||||
import { log } from 'electron-log/main';
|
||||
import { IPC } from './shared-with-frontend/ipc-events.const';
|
||||
import { getDistChannel } from './shared-with-frontend/get-dist-channel';
|
||||
import { getIsTrayShowCurrentTask, getIsTrayShowCurrentCountdown } from './shared-state';
|
||||
import { TaskCopy } from '../src/app/features/tasks/task.model';
|
||||
import { release } from 'os';
|
||||
|
|
@ -76,10 +77,11 @@ const WINDOWS_TRAY_GUIDS = {
|
|||
} as const;
|
||||
|
||||
const getWindowsTrayGuid = (): string => {
|
||||
if (process.env.PORTABLE_EXECUTABLE_DIR) {
|
||||
const channel = getDistChannel();
|
||||
if (channel === 'win-portable') {
|
||||
return WINDOWS_TRAY_GUIDS.portable;
|
||||
}
|
||||
if ((process as NodeJS.Process & { windowsStore?: boolean }).windowsStore) {
|
||||
if (channel === 'win-store') {
|
||||
return WINDOWS_TRAY_GUIDS.store;
|
||||
}
|
||||
return WINDOWS_TRAY_GUIDS.nsis;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ import {
|
|||
} from 'electron';
|
||||
import { ElectronAPI } from './electronAPI.d';
|
||||
import { IPC, IPCEventValue } from './shared-with-frontend/ipc-events.const';
|
||||
import {
|
||||
getDistChannel,
|
||||
ElectronDistChannel,
|
||||
} from './shared-with-frontend/get-dist-channel';
|
||||
import { LocalBackupMeta } from '../src/app/imex/local-backup/local-backup.model';
|
||||
import {
|
||||
PluginManifest,
|
||||
|
|
@ -52,6 +56,10 @@ const ea: ElectronAPI = {
|
|||
// NOTE: there is no proper way to unsubscribe apart from unsubscribing all
|
||||
ipcRenderer.on(channel, listener);
|
||||
},
|
||||
// SYNC
|
||||
// ----
|
||||
getDistChannel: (): ElectronDistChannel | null => getDistChannel(),
|
||||
|
||||
// INVOKE
|
||||
// ------
|
||||
getUserDataPath: () => _invoke('GET_PATH', 'userData') as Promise<string>,
|
||||
|
|
|
|||
51
electron/shared-with-frontend/get-dist-channel.ts
Normal file
51
electron/shared-with-frontend/get-dist-channel.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Distribution channels detectable from the Electron process.
|
||||
* Mobile (android/ios) and plain web are determined in the frontend instead.
|
||||
*/
|
||||
export type ElectronDistChannel =
|
||||
| 'win-nsis'
|
||||
| 'win-portable'
|
||||
| 'win-store'
|
||||
| 'mac-dmg'
|
||||
| 'mac-store'
|
||||
| 'linux-appimage'
|
||||
| 'linux-snap'
|
||||
| 'linux-flatpak'
|
||||
| 'linux-native';
|
||||
|
||||
/**
|
||||
* Detects which distribution channel the running Electron app belongs to.
|
||||
* Returns `null` on unknown platforms.
|
||||
*
|
||||
* Shared between the Electron main process (tray GUID selection) and the
|
||||
* preload bridge (channel suffix appended to the diagnostic version string).
|
||||
* Safe to call from preload: only reads `process` / `process.env`.
|
||||
*
|
||||
* Note: deb vs rpm cannot be told apart at runtime (electron-builder sets no
|
||||
* env var for native packages), so both report `linux-native`.
|
||||
*/
|
||||
export const getDistChannel = (): ElectronDistChannel | null => {
|
||||
const p = process as NodeJS.Process & { windowsStore?: boolean; mas?: boolean };
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
if (process.env.PORTABLE_EXECUTABLE_DIR) {
|
||||
return 'win-portable';
|
||||
}
|
||||
return p.windowsStore ? 'win-store' : 'win-nsis';
|
||||
case 'darwin':
|
||||
return p.mas ? 'mac-store' : 'mac-dmg';
|
||||
case 'linux':
|
||||
if (process.env.APPIMAGE) {
|
||||
return 'linux-appimage';
|
||||
}
|
||||
if (process.env.SNAP) {
|
||||
return 'linux-snap';
|
||||
}
|
||||
if (process.env.FLATPAK_ID) {
|
||||
return 'linux-flatpak';
|
||||
}
|
||||
return 'linux-native';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
39
src/app/util/get-app-version-str.spec.ts
Normal file
39
src/app/util/get-app-version-str.spec.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { DistChannel, distChannelSuffix, getAppVersionStr } from './get-app-version-str';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
describe('distChannelSuffix', () => {
|
||||
const cases: [DistChannel, string][] = [
|
||||
['win-nsis', 'W'],
|
||||
['win-portable', 'P'],
|
||||
['win-store', 'MS'],
|
||||
['mac-dmg', 'D'],
|
||||
['mac-store', 'MAS'],
|
||||
['linux-appimage', 'AI'],
|
||||
['linux-snap', 'SN'],
|
||||
['linux-flatpak', 'FP'],
|
||||
['linux-native', 'L'],
|
||||
['android-play', 'A'],
|
||||
['android-fdroid', 'AF'],
|
||||
['ios', 'I'],
|
||||
['web', 'WB'],
|
||||
];
|
||||
|
||||
cases.forEach(([channel, suffix]) => {
|
||||
it(`maps ${channel} -> "${suffix}"`, () => {
|
||||
expect(distChannelSuffix(channel)).toBe(suffix);
|
||||
});
|
||||
});
|
||||
|
||||
it('maps null/undefined -> "" (no suffix)', () => {
|
||||
expect(distChannelSuffix(null)).toBe('');
|
||||
expect(distChannelSuffix(undefined)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAppVersionStr', () => {
|
||||
// In the Karma/browser env IS_ELECTRON, IS_IOS and IS_ANDROID_WEB_VIEW are
|
||||
// all false, so the channel resolves to web -> "WB".
|
||||
it('appends the web suffix in a browser context', () => {
|
||||
expect(getAppVersionStr()).toBe(`${environment.version}WB`);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,9 +1,81 @@
|
|||
import { IS_ANDROID_WEB_VIEW } from './is-android-web-view';
|
||||
import { IS_ANDROID_WEB_VIEW, IS_F_DROID_APP } from './is-android-web-view';
|
||||
import { IS_IOS } from './is-ios';
|
||||
import { IS_ELECTRON } from '../app.constants';
|
||||
import { androidInterface } from '../features/android/android-interface';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
export const getAppVersionStr = (): string => {
|
||||
const b =
|
||||
(IS_ANDROID_WEB_VIEW && androidInterface?.getVersion?.()) || environment.version;
|
||||
return IS_ANDROID_WEB_VIEW ? `${b}A` : b;
|
||||
/**
|
||||
* Every distribution target the app ships to. Mobile/web are detected in the
|
||||
* frontend; desktop ones come from the Electron `getDistChannel()` bridge.
|
||||
*/
|
||||
export type DistChannel =
|
||||
| 'win-nsis'
|
||||
| 'win-portable'
|
||||
| 'win-store'
|
||||
| 'mac-dmg'
|
||||
| 'mac-store'
|
||||
| 'linux-appimage'
|
||||
| 'linux-snap'
|
||||
| 'linux-flatpak'
|
||||
| 'linux-native'
|
||||
| 'android-play'
|
||||
| 'android-fdroid'
|
||||
| 'ios'
|
||||
| 'web';
|
||||
|
||||
/**
|
||||
* Channel marker appended to the (display-only) version string so bug reports
|
||||
* and the config footer reveal which build a user runs, e.g. `18.6.0AI` for
|
||||
* the Linux AppImage. Display-only: no caller does a semver compare on this.
|
||||
*/
|
||||
export const distChannelSuffix = (channel: DistChannel | null | undefined): string => {
|
||||
switch (channel) {
|
||||
case 'win-nsis':
|
||||
return 'W';
|
||||
case 'win-portable':
|
||||
return 'P';
|
||||
case 'win-store':
|
||||
return 'MS';
|
||||
case 'mac-dmg':
|
||||
return 'D';
|
||||
case 'mac-store':
|
||||
return 'MAS';
|
||||
case 'linux-appimage':
|
||||
return 'AI';
|
||||
case 'linux-snap':
|
||||
return 'SN';
|
||||
case 'linux-flatpak':
|
||||
return 'FP';
|
||||
case 'linux-native':
|
||||
return 'L';
|
||||
case 'android-play':
|
||||
return 'A';
|
||||
case 'android-fdroid':
|
||||
return 'AF';
|
||||
case 'ios':
|
||||
return 'I';
|
||||
case 'web':
|
||||
return 'WB';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const detectChannel = (): DistChannel => {
|
||||
if (IS_IOS) {
|
||||
return 'ios';
|
||||
}
|
||||
if (IS_ANDROID_WEB_VIEW) {
|
||||
return IS_F_DROID_APP ? 'android-fdroid' : 'android-play';
|
||||
}
|
||||
if (IS_ELECTRON && typeof window !== 'undefined') {
|
||||
return window.ea?.getDistChannel?.() ?? 'web';
|
||||
}
|
||||
return 'web';
|
||||
};
|
||||
|
||||
export const getAppVersionStr = (): string => {
|
||||
const base =
|
||||
(IS_ANDROID_WEB_VIEW && androidInterface?.getVersion?.()) || environment.version;
|
||||
return `${base}${distChannelSuffix(detectChannel())}`;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue