Skip to content

Commit 01f78f3

Browse files
authored
Merge pull request #9549 from wireapp/staging
Release: v3.41 [WPB-23862]
2 parents cc5bbc3 + e03a9e0 commit 01f78f3

File tree

17 files changed

+1862
-932
lines changed

17 files changed

+1862
-932
lines changed

.github/workflows/build_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
runs-on: ubuntu-latest
1313
steps:
1414
- name: Cancel Previous Runs
15-
uses: styfle/cancel-workflow-action@0.12.1
15+
uses: styfle/cancel-workflow-action@0.13.1
1616
with:
1717
access_token: ${{github.token}}
1818
- name: Checkout repository

.github/workflows/dependabot-auto-merge.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
steps:
1313
- name: Dependabot metadata
1414
id: metadata
15-
uses: dependabot/fetch-metadata@v2.4.0
15+
uses: dependabot/fetch-metadata@v2.5.0
1616
with:
1717
github-token: '${{secrets.WEBTEAM_AUTOMERGE_TOKEN}}'
1818

.github/workflows/label.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ jobs:
1111
steps:
1212
# The settings for this action are in `.github/labeler.yml`.
1313
# See https://github.com/srvaroa/labeler.
14-
- uses: srvaroa/labeler@0a20eccb8c94a1ee0bed5f16859aece1c45c3e55
14+
- uses: srvaroa/labeler@bf262763a8a8e191f5847873aecc0f29df84f957
1515
env:
1616
GITHUB_TOKEN: ${{secrets.OTTO_THE_BOT_GH_TOKEN}}

app-config/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"dependencies": {
3-
"wire-web-config-internal": "https://github.com/wireapp/wire-web-config-default#v0.34.6",
3+
"wire-web-config-internal": "https://github.com/wireapp/wire-web-config-default#v0.34.10",
44
"wire-web-config-production": "https://github.com/wireapp/wire-web-config-wire#v0.34.6"
55
}
66
}

bin/deploy-tools/lib/deploy-utils.test.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,21 @@
1717
*/
1818

1919
import * as assert from 'assert';
20-
2120
import {find} from './deploy-utils';
2221

2322
describe('deploy-utils', () => {
2423
it('safeguard prevents further code execution', async () => {
24+
// safeGuard: true should throw an error
2525
try {
26-
await find('invalid-file', {safeGuard: true});
26+
await find('invalid-file', {cwd: __dirname, safeGuard: true});
2727
assert.fail('find should throw an error with safeguard');
28-
} catch (error) {}
28+
} catch (error) {
29+
// expected
30+
}
2931

32+
// safeGuard: false should return null instead of throwing
3033
try {
31-
const result = await find('invalid-file', {safeGuard: false});
34+
const result = await find('invalid-file', {cwd: __dirname, safeGuard: false});
3235
assert.strictEqual(result, null);
3336
} catch (error) {
3437
assert.fail('find should not throw an error without safeguard');

electron/src/locale/index.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ export type SupportedI18nLanguageObject = Record<SupportedI18nLanguage, i18nStri
5555

5656
const app = Electron.app || require('@electron/remote').app;
5757

58+
const parseLocale = (locale: string): SupportedI18nLanguage => {
59+
const languageKeys = Object.keys(SUPPORTED_LANGUAGES) as SupportedI18nLanguage[];
60+
return languageKeys.find(languageKey => languageKey === locale) || languageKeys[0];
61+
};
62+
63+
const getSystemLocale = (): SupportedI18nLanguage => parseLocale(app.getLocale().substring(0, 2));
64+
5865
export const LANGUAGES: SupportedI18nLanguageObject = {
5966
cs,
6067
da,
@@ -139,17 +146,24 @@ export const SUPPORTED_LANGUAGES = {
139146
let current: SupportedI18nLanguage | undefined;
140147

141148
export const getCurrent = (): SupportedI18nLanguage => {
149+
const systemLocale = getSystemLocale();
150+
142151
if (!current) {
143-
// We care only about the language part and not the country (en_US, de_DE)
144-
const defaultLocale = parseLocale(app.getLocale().substring(0, 2));
145-
current = settings.restore(SettingsType.LOCALE, defaultLocale);
152+
const savedLocale = settings.restore<SupportedI18nLanguage | undefined>(SettingsType.LOCALE);
153+
const savedOverride = settings.restore<boolean | undefined>(SettingsType.LOCALE_OVERRIDE);
154+
const hasUserOverride =
155+
typeof savedOverride === 'boolean' ? savedOverride : Boolean(savedLocale && savedLocale !== systemLocale);
156+
157+
current = savedLocale && hasUserOverride ? parseLocale(savedLocale) : systemLocale;
158+
return current;
146159
}
147-
return current;
148-
};
149160

150-
const parseLocale = (locale: string): SupportedI18nLanguage => {
151-
const languageKeys = Object.keys(SUPPORTED_LANGUAGES) as SupportedI18nLanguage[];
152-
return languageKeys.find(languageKey => languageKey === locale) || languageKeys[0];
161+
// If there’s no override and the system locale changed, update the cache
162+
const hasOverride = settings.restore<boolean | undefined>(SettingsType.LOCALE_OVERRIDE) === true;
163+
if (!hasOverride && current !== systemLocale) {
164+
current = systemLocale;
165+
}
166+
return current;
153167
};
154168

155169
const customReplacements: Record<string, string> = {
@@ -180,5 +194,15 @@ export const getText = (
180194

181195
export const setLocale = (locale: string): void => {
182196
current = parseLocale(locale);
183-
settings.save(SettingsType.LOCALE, current);
197+
198+
const systemLocale = getSystemLocale();
199+
const isOverride = current !== systemLocale;
200+
201+
if (isOverride) {
202+
settings.save(SettingsType.LOCALE_OVERRIDE, true);
203+
settings.save(SettingsType.LOCALE, current);
204+
} else {
205+
settings.delete(SettingsType.LOCALE_OVERRIDE);
206+
settings.delete(SettingsType.LOCALE);
207+
}
184208
};

electron/src/main.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import {config} from './settings/config';
6969
import {settings} from './settings/ConfigurationPersistence';
7070
import {SettingsType} from './settings/SettingsType';
7171
import {SingleSignOn} from './sso/SingleSignOn';
72+
import {initMacAutoUpdater} from './update/macosAutoUpdater';
7273
import {AboutWindow} from './window/AboutWindow';
7374
import {ProxyPromptWindow} from './window/ProxyPromptWindow';
7475
import {WindowManager} from './window/WindowManager';
@@ -105,6 +106,7 @@ const currentLocale = locale.getCurrent();
105106
const startHidden = Boolean(argv[config.ARGUMENT.STARTUP] || argv[config.ARGUMENT.HIDDEN]);
106107
const customDownloadPath = settings.restore<string | undefined>(SettingsType.DOWNLOAD_PATH);
107108
const appHomePath = (path: string) => `${app.getPath('home')}\\${path}`;
109+
const isInternalBuild = (): boolean => config.environment === 'internal';
108110

109111
if (customDownloadPath) {
110112
electronDl({
@@ -469,6 +471,7 @@ const handleAppEvents = (): void => {
469471
const mainWindowState = initWindowStateKeeper();
470472
const appMenu = systemMenu.createMenu(isFullScreen);
471473
if (EnvironmentUtil.app.IS_DEVELOPMENT) {
474+
app.commandLine.appendSwitch('enable-webrtc-internals');
472475
appMenu.append(developerMenu);
473476
}
474477

@@ -478,6 +481,25 @@ const handleAppEvents = (): void => {
478481
tray.initTray();
479482
}
480483
await showMainWindow(mainWindowState);
484+
485+
app.on('ready', async () => {
486+
const mainWindowState = initWindowStateKeeper();
487+
const appMenu = systemMenu.createMenu(isFullScreen);
488+
if (EnvironmentUtil.app.IS_DEVELOPMENT) {
489+
appMenu.append(developerMenu);
490+
}
491+
492+
Menu.setApplicationMenu(appMenu);
493+
tray = new TrayHandler();
494+
if (!EnvironmentUtil.platform.IS_MAC_OS) {
495+
tray.initTray();
496+
}
497+
await showMainWindow(mainWindowState);
498+
499+
if (EnvironmentUtil.platform.IS_MAC_OS && isInternalBuild()) {
500+
initMacAutoUpdater(main);
501+
}
502+
});
481503
});
482504
};
483505

electron/src/menu/developer.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*
1818
*/
1919

20-
import {MenuItem, MenuItemConstructorOptions} from 'electron';
20+
import {BrowserWindow, MenuItem, MenuItemConstructorOptions} from 'electron';
2121

2222
import {executeJavaScriptWithoutResult} from '../lib/ElectronUtil';
2323
import {getAvailebleEnvironments, setEnvironment} from '../runtime/EnvironmentUtil';
@@ -106,13 +106,37 @@ const separatorTemplate: MenuItemConstructorOptions = {
106106
type: 'separator',
107107
};
108108

109+
const openWebRTCInternals = () => {
110+
const win = new BrowserWindow({
111+
width: 1200,
112+
height: 800,
113+
webPreferences: {
114+
nodeIntegration: false,
115+
contextIsolation: true,
116+
},
117+
});
118+
119+
win.webContents.setWindowOpenHandler(() => ({
120+
action: 'allow',
121+
}));
122+
123+
win.loadURL('chrome://webrtc-internals/');
124+
};
125+
126+
const webRTCInternalsTemplate: MenuItemConstructorOptions = {
127+
label: 'Toggle WebRTC Internals',
128+
click: () => openWebRTCInternals(),
129+
};
130+
109131
const menuTemplate: MenuItemConstructorOptions = {
110132
id: 'Developer',
111133
label: '&Developer',
112134
submenu: [
113135
devToolsTemplate,
114136
reloadTemplate,
115137
separatorTemplate,
138+
webRTCInternalsTemplate,
139+
separatorTemplate,
116140
{
117141
enabled: false,
118142
label: 'Environment',

electron/src/settings/SettingsType.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export enum SettingsType {
4646
FULL_SCREEN = 'fullscreen',
4747
/** Which language (ISO 639-1) should be used to load our web app (de, en, fr, etc.)? */
4848
LOCALE = 'locale',
49+
/** Whether the locale has been explicitly set by the user instead of using the system default. */
50+
LOCALE_OVERRIDE = 'localeOverride',
4951
/** Defines the proxy server url (e.g. http://127.0.0.1:3128)
5052
*
5153
* https://github.com/wireapp/wire-desktop/wiki/Start-Parameters#use-authenticated-proxy-server
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2025 Wire Swiss GmbH
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see http://www.gnu.org/licenses/.
17+
*
18+
*/
19+
20+
import {BrowserWindow, dialog} from 'electron';
21+
import {autoUpdater} from 'electron-updater';
22+
23+
import {getLogger} from '../logging/getLogger';
24+
import {config} from '../settings/config';
25+
26+
const logger = getLogger('MacAutoUpdater');
27+
const isInternalBuild = (): boolean => config.environment === 'internal';
28+
29+
export function initMacAutoUpdater(mainWindow: BrowserWindow): void {
30+
// Skip in dev
31+
if (process.env.NODE_ENV === 'development') {
32+
logger.log('Skipping auto-update in development');
33+
return;
34+
}
35+
36+
// Only run for internal builds (production = App Store -> handled by Apple)
37+
if (!isInternalBuild()) {
38+
logger.log('Skipping auto-update: not an internal build');
39+
return;
40+
}
41+
42+
logger.log('Initializing macOS auto-updater for internal build');
43+
44+
// INTERNAL FEED URL (served from S3)
45+
// We upload latest-mac.yml + WireInternal-<version>.zip here.
46+
const feedUrl =
47+
process.env.WIRE_INTERNAL_MAC_UPDATE_URL || 'https://wire-taco.s3.eu-west-1.amazonaws.com/mac/internal/updates/';
48+
49+
// TODO (infra): once internal mac auto-update is validated using the raw S3 URL,
50+
// expose it via https://wire-app.wire.com/mac/internal/updates/ (similar to WIN_URL_UPDATE).
51+
// const feedUrl =
52+
// process.env.WIRE_INTERNAL_MAC_UPDATE_URL ||
53+
// 'https://wire-app.wire.com/mac/internal/updates/';
54+
55+
logger.log(`Using update feed: ${feedUrl}`);
56+
57+
autoUpdater.setFeedURL({
58+
provider: 'generic',
59+
url: feedUrl,
60+
});
61+
62+
autoUpdater.on('checking-for-update', () => {
63+
logger.log('Checking for update…');
64+
});
65+
66+
autoUpdater.on('update-available', info => {
67+
logger.log(`Update available: ${info.version}`);
68+
});
69+
70+
autoUpdater.on('update-not-available', info => {
71+
logger.log(`No update available (current: ${info.version})`);
72+
});
73+
74+
autoUpdater.on('error', err => {
75+
logger.error('Auto-updater error', err);
76+
});
77+
78+
autoUpdater.on('download-progress', progress => {
79+
// For now we only log; no progress UI.
80+
logger.log(
81+
`Update download progress: ${progress.percent?.toFixed?.(1) ?? 'n/a'}% at ${progress.bytesPerSecond} B/s`,
82+
);
83+
});
84+
85+
autoUpdater.on('update-downloaded', async info => {
86+
logger.log(`Update downloaded: ${info.version}`);
87+
88+
// Show a simple native dialog when the update is ready.
89+
const result = await dialog.showMessageBox(mainWindow, {
90+
type: 'info',
91+
buttons: ['Install & Restart', 'Later'],
92+
defaultId: 0,
93+
cancelId: 1,
94+
title: 'Wire Update Ready',
95+
message: 'A new version of Wire is ready to install.',
96+
detail: `Version ${info.version} has been downloaded. Wire will restart to complete the update.`,
97+
});
98+
99+
if (result.response === 0) {
100+
logger.log('User chose to install update now');
101+
autoUpdater.quitAndInstall();
102+
} else {
103+
logger.log('User chose to install later');
104+
}
105+
});
106+
107+
// Kick off background check + download.
108+
autoUpdater.checkForUpdates();
109+
}

0 commit comments

Comments
 (0)