Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
runtime = electron
disturl = https://electronjs.org/headers
target = 27.3.11
target = 33.4.11
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18.15.0
20.20.2
12 changes: 0 additions & 12 deletions .yarn/patches/windows-focus-assist-npm-1.3.0-e02310a164.patch

This file was deleted.

10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,18 @@
"devDependencies": {
"rimraf": "^5.0.10"
},
"resolutions": {
"node-gyp": "12.4.0",
"react-draggable": "4.4.6",
"@electron/rebuild": "3.7.2",
"@electron/node-gyp": "npm:node-gyp@12.4.0"
},
"devEngines": {
"node": ">=18.0.0",
"node": ">=20.0.0",
"yarn": ">=4.1.0"
},
"volta": {
"node": "18.14.0",
"node": "20.20.2",
"yarn": "4.1.0"
},
"packageManager": "yarn@4.1.0"
Expand Down
Binary file modified packages/app/build/icon.ico
Binary file not shown.
6 changes: 4 additions & 2 deletions packages/app/electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ publish:
provider: 'github'

asar: true
asarUnpack:
- 'node_modules/**/*.{node,dll}'
# No explicit asarUnpack: an unpack pattern makes app-builder-lib resolve every
# packaged file against the app dir, which throws "must be under" for
# workspace deps symlinked outside packages/app (@getstation/sdk, appstore).
# electron-builder's smartUnpack already unpacks any package holding .node/.dll.

afterPack: '../../scripts/after-pack.js'
# afterSign: '../../scripts/notarize.js'
Expand Down
28 changes: 14 additions & 14 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
"sass": "sass src/theme/scss/app.global.scss src/theme/css/app.global.css",
"clean-gql-gen": "rm -f src/**/*.generated.ts ||: && rm -f src/**/*.generated.tsx ||:",
"gql-gen": "yarn run clean-gql-gen && graphql-codegen --config codegen-local.yml",
"build-webpack:webui:dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=development webpack --config webpack.config.webui.js --colors --no-progress --mode=development",
"build-webpack:webui:prod": "cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=production webpack --config webpack.config.webui.js --colors --mode=production --no-progress",
"build-webpack:prod": "yarn run gql-gen && yarn run sass && rimraf dist && cross-env NODE_OPTIONS=--openssl-legacy-provider electron-webpack --no-progress && yarn run build-webpack:webui:prod",
"build-webpack:webui:dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider DISABLE_V8_COMPILE_CACHE=1 NODE_ENV=development webpack --config webpack.config.webui.js --colors --no-progress --mode=development",
"build-webpack:webui:prod": "cross-env NODE_OPTIONS=--openssl-legacy-provider DISABLE_V8_COMPILE_CACHE=1 NODE_ENV=production webpack --config webpack.config.webui.js --colors --mode=production --no-progress",
"build-webpack:prod": "yarn run gql-gen && yarn run sass && rimraf dist && cross-env NODE_OPTIONS=--openssl-legacy-provider DISABLE_V8_COMPILE_CACHE=1 electron-webpack --no-progress && yarn run build-webpack:webui:prod",
"clean": "yarn run clean-gql-gen && rimraf dist",
"build": "yarn run build-webpack:prod",
"release": "yarn build && cross-env NODE_ENV=production electron-builder",
"dev": "yarn run gql-gen && yarn run sass && yarn run build-webpack:webui:dev && electron-webpack dev",
"dev": "yarn run gql-gen && yarn run sass && yarn run build-webpack:webui:dev && cross-env DISABLE_V8_COMPILE_CACHE=1 electron-webpack dev",
"lint": "yarn run gql-gen && tslint -p .",
"test": "cross-env NODE_ENV=test jest --runInBand"
},
Expand All @@ -37,7 +37,7 @@
"1password-node": "^3.0.0",
"@babel/plugin-proposal-decorators": "^7.4.4",
"@babel/runtime": "^7.4.3",
"@electron/remote": "^2.1.2",
"@electron/remote": "^2.1.3",
"@getstation/apollo-link-reactive-schema": "^1.0.0",
"@getstation/electron-google-oauth2": "^14.0.0",
"@getstation/fetch-favicon": "^0.0.4-rc.2",
Expand Down Expand Up @@ -66,7 +66,7 @@
"dom-scroll-into-view": "^1.2.1",
"dotenv": "^16.0.3",
"electron-better-web-request": "^1.0.1",
"electron-chrome-extension": "https://github.com/viktor44/electron-chrome-extension/releases/download/v6.0.3/release.tar.gz",
"electron-chrome-extension": "file:.yalc/electron-chrome-extension",
"electron-debug": "^3.2.0",
"electron-log": "^2.2.14",
"electron-process-manager": "^1.2.0",
Expand Down Expand Up @@ -169,8 +169,8 @@
"slack": "^11.0.2",
"source-map-support": "^0.5.21",
"sqlite3": "5.1.6",
"stream-electron-ipc": "^14.1.0",
"stream-json-rpc": "^0.9.1",
"stream-electron-ipc": "file:.yalc/stream-electron-ipc",
"stream-json-rpc": "file:.yalc/stream-json-rpc",
"suncalc": "^1.9.0",
"tinycolor2": "^1.4.1",
"tough-cookie": "^2.4.3",
Expand All @@ -187,7 +187,7 @@
"use-timeout": "^1.1.0",
"utility-types": "^3.7.0",
"uuid": "^3.3.3",
"windows-focus-assist": "patch:windows-focus-assist@npm%3A1.3.0#~/.yarn/patches/windows-focus-assist-npm-1.3.0-e02310a164.patch",
"windows-focus-assist": "1.4.0",
"ws": "^5.2.2"
},
"devDependencies": {
Expand Down Expand Up @@ -284,9 +284,9 @@
"copy-webpack-plugin": "^4.6.0",
"cross-env": "^6.0.3",
"css-loader": "^3.2.1",
"electron": "27.3.11",
"electron-builder": "^24.9.1",
"electron-mocha": "^12.1.0",
"electron": "33.4.11",
"electron-builder": "^25.1.8",
"electron-mocha": "^13.0.0",
"electron-webpack": "^2.8.2",
"eslint": "^4.18.2",
"eslint-config-airbnb": "^16.1.0",
Expand Down Expand Up @@ -314,7 +314,7 @@
"mini-css-extract-plugin": "^0.8.0",
"morgan": "^1.8.2",
"node-fetch": "^2.6.7",
"node-gyp": "^9.4.0",
"node-gyp": "12.4.0",
"npm-run": "^4.1.2",
"raw-loader": "^4.0.2 ",
"react-addons-perf": "^15.4.2",
Expand All @@ -323,7 +323,7 @@
"remote-redux-devtools": "0.5.0",
"replace-in-file": "^4.1.3",
"rimraf": "^5.0.10",
"sass": "^1.34.1",
"sass": ">=1.34.1 <1.100.0",
"simple-git": "^1.107.0",
"sinon": "^7.1.1",
"style-loader": "^0.23.1",
Expand Down
7 changes: 6 additions & 1 deletion packages/app/src/app/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,12 @@ function* sagaLoadingScreen() {
yield put(setLoadingScreenVisibility(true));

// we let the onboarding flow deal with removing the onbaording screen
yield take(REHYDRATION_COMPLETE);
// safety net: never keep the loading screen forever if REHYDRATION_COMPLETE was
// already dispatched before this saga started
yield race({
rehydrated: take(REHYDRATION_COMPLETE),
timeout: delay(ms('20sec')),
});
const loggedInAndReady = yield select(isFullyReady);
if (!loggedInAndReady) {
yield put(setLoadingScreenVisibility(false));
Expand Down
12 changes: 9 additions & 3 deletions packages/app/src/applications/Application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ class ApplicationImpl extends React.PureComponent {
this.setWebviewRef = this.setWebviewRef.bind(this);
this.handleTitleUpdated = this.handleTitleUpdated.bind(this);
this.handleFaviconUpdated = this.handleFaviconUpdated.bind(this);
this.handleWebcontentsCrashed = this.handleWebcontentsCrashed.bind(this);
this.handleRenderProcessGone = this.handleRenderProcessGone.bind(this);
this.handleRemoveApplication = this.handleRemoveApplication.bind(this);

this.state = {
Expand Down Expand Up @@ -385,7 +385,13 @@ class ApplicationImpl extends React.PureComponent {
}
}

handleWebcontentsCrashed() {
/**
* Replaces the `crashed` event, removed in Electron 29.
* `render-process-gone` also fires on a normal shutdown, which is not a crash.
*/
handleRenderProcessGone(event: Electron.RenderProcessGoneEvent) {
const details = event && event.details;
if (details && details.reason === 'clean-exit') return;
this.props.onWebcontentsCrashed();
}

Expand Down Expand Up @@ -476,7 +482,7 @@ class ApplicationImpl extends React.PureComponent {
onDidStopLoading={this.handleDidStopLoading}
onDidFailLoad={this.handleDidFailLoad}
onDomReady={this.handleDomReady}
onCrashed={this.handleWebcontentsCrashed}
onRenderProcessGone={this.handleRenderProcessGone}
webpreferences={`allowRunningInsecureContent=true,nativeWindowOpen=${useNativeWindowOpen},contextIsolation=true,nodeIntegration=true`}
/>

Expand Down
10 changes: 4 additions & 6 deletions packages/app/src/common/components/ElectronWebview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ export interface ElectronWebviewProps extends Omit<Electron.WebviewTag, 'src'> {
onNewWindow?: EventListener;
onClose?: EventListener;
onIpcMessage?: EventListener;
onCrashed?: EventListener;
onGpuCrashed?: EventListener;
onPluginCrashed?: EventListener;
onRenderProcessGone?: EventListener;
onDestroyed?: EventListener;
}

Expand Down Expand Up @@ -73,9 +71,9 @@ export const events = [
'did-navigate-in-page',
'close',
'ipc-message',
'crashed',
'gpu-crashed',
'plugin-crashed',
// `crashed`, `gpu-crashed` and `plugin-crashed` have been removed in Electron 29
// @see https://www.electronjs.org/docs/latest/breaking-changes#removed-crashed-event-on-webcontents-and-webview
'render-process-gone',
'destroyed',
'media-started-playing',
'media-paused',
Expand Down
11 changes: 4 additions & 7 deletions packages/app/src/lib/ipc-renderer-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,10 @@ ipcRendererInternal.sendSync = function (channel: any, ...args: any[]) {
return ipc.sendSync(internal, channel, args)[0];
};

ipcRendererInternal.sendTo = function (webContentsId: any, channel: any, ...args: any[]) {
return ipc.sendTo(internal, false, webContentsId, channel, args);
};

ipcRendererInternal.sendToAll = function (webContentsId: any, channel: any, ...args: any[]) {
return ipc.sendTo(internal, true, webContentsId, channel, args);
};
// `sendTo` and `sendToAll` are gone: the `sendTo` method of the `electron_renderer_ipc`
// binding has been removed in Electron 28, along with `ipcRenderer.sendTo`.
// Nothing here used them, see `lib/ipc-send-to.ts` for the replacement.
// @see https://www.electronjs.org/docs/latest/breaking-changes#removed-ipcrenderersendto

ipcRendererInternal.invoke = async function (channel: string, ...args: any[]) {
const { error, result } = await ipc.invoke(internal, channel, args);
Expand Down
71 changes: 71 additions & 0 deletions packages/app/src/lib/ipc-send-to.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { ipcMain, ipcRenderer, webContents } from 'electron';

/** renderer -> main: forward a message to another webContents */
const RELAY = 'bx.relay-to-webcontents';
/** renderer -> main: same, but the target also gets the sender webContents id */
const RELAY_WITH_SENDER = 'bx.relay-to-webcontents-with-sender';

/**
* Renderer to renderer messaging.
*
* `ipcRenderer.sendTo` and `IpcRendererEvent.senderId` have been removed in Electron 28,
* so the main process now relays those messages.
* @see https://www.electronjs.org/docs/latest/breaking-changes#removed-ipcrenderersendto
*/

/**
* Starts the relay. Must be called once from the main process, before any renderer
* uses `sendToWebContents` / `sendToWebContentsWithSender`.
*/
export const startIpcSendToRelay = () => {
const forward = (targetWebContentsId: number, channel: string, args: any[]) => {
const target = webContents.fromId(targetWebContentsId);
if (!target || target.isDestroyed() || target.isCrashed()) return;
target.send(channel, ...args);
};

ipcMain.on(RELAY, (_event: Electron.IpcMainEvent, targetWebContentsId: number, channel: string, args: any[]) => {
forward(targetWebContentsId, channel, args);
});

ipcMain.on(
RELAY_WITH_SENDER,
(event: Electron.IpcMainEvent, targetWebContentsId: number, channel: string, args: any[]) => {
forward(targetWebContentsId, channel, [event.sender.id, ...args]);
}
);
};

/**
* From a renderer process, send a message to another webContents.
* Drop-in replacement for `ipcRenderer.sendTo`: the receiver signature is unchanged.
*/
export const sendToWebContents = (targetWebContentsId: number, channel: string, ...args: any[]) => {
ipcRenderer.send(RELAY, targetWebContentsId, channel, args);
};

/**
* Same as {@link sendToWebContents}, but the receiver also gets the sender webContents id
* as first argument, which used to be read from `event.senderId`.
* The receiver must use {@link onFromWebContents}.
*/
export const sendToWebContentsWithSender = (targetWebContentsId: number, channel: string, ...args: any[]) => {
ipcRenderer.send(RELAY_WITH_SENDER, targetWebContentsId, channel, args);
};

/**
* Receiving counterpart of {@link sendToWebContentsWithSender}.
* Returns a function removing the listener.
*/
export const onFromWebContents = (
channel: string,
listener: (senderWebContentsId: number, ...args: any[]) => void
) => {
const handler = (_event: Electron.IpcRendererEvent, senderWebContentsId: number, ...args: any[]) => {
listener(senderWebContentsId, ...args);
};
ipcRenderer.on(channel, handler);
return () => {
ipcRenderer.removeListener(channel, handler);
};
};
7 changes: 5 additions & 2 deletions packages/app/src/plugins/webview-preload.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { contextBridge, ipcRenderer } = require('electron');
const { Observable } = require('rxjs');
const { sendToWebContentsWithSender } = require('../lib/ipc-send-to');

const sendPerformToProxy = (channel, payload) => {
const p = new Promise(resolve => {
Expand All @@ -9,7 +10,8 @@
});
setTimeout(() => {
ipcRenderer.invoke('get-worker-contents-id')
.then(workerWebContentsId => ipcRenderer.sendTo(workerWebContentsId, 'bx-api-perform', channel, payload));
.then(workerWebContentsId =>
sendToWebContentsWithSender(workerWebContentsId, 'bx-api-perform', channel, payload));
}, 1);
return p;
};
Expand All @@ -18,7 +20,8 @@
ipcRenderer.on(`bx-api-subscribe-response-${channel}`, listener);
setTimeout(() => {
ipcRenderer.invoke('get-worker-contents-id')
.then(workerWebContentsId => ipcRenderer.sendTo(workerWebContentsId, 'bx-api-subscribe', channel));
.then(workerWebContentsId =>
sendToWebContentsWithSender(workerWebContentsId, 'bx-api-subscribe', channel));
}, 1);
};

Expand Down
6 changes: 5 additions & 1 deletion packages/app/src/services/main/boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
import { app, ipcMain } from 'electron';

import { startSessionsListening } from '../api/sessions';
import { startIpcSendToRelay } from '../../lib/ipc-send-to';

export default () => {

// make sure that `stream-electron-ipc` side-effect is called early
require('stream-electron-ipc');

// relays renderer to renderer messages, `ipcRenderer.sendTo` is gone since Electron 28
startIpcSendToRelay();

startSessionsListening();

ipcMain.on('get-is-packaged', (event) => {
Expand Down
18 changes: 7 additions & 11 deletions packages/app/src/services/services/sdkv2/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { distinctUntilChanged, map, share } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { uninstallAllInstances } from '../../../abstract-application/duck';
import { setConfigData, uninstallApplication } from '../../../applications/duck';
import { onFromWebContents, sendToWebContents } from '../../../lib/ipc-send-to';
import { getSnoozeDurationInMs } from '../../../notification-center/selectors';
import { getThemeColors } from '../../../theme/selectors';
import { StationState, StationStoreWorker } from '../../../types';
Expand Down Expand Up @@ -175,17 +176,13 @@ export class SDKv2ServiceImpl extends SDKv2Service implements RPC.Interface<SDKv
}
}

const getSenderId = (e: any) => typeof e.senderId === 'number' ? e.senderId :
typeof e.sender.id === 'number' ? e.sender.id : 0;

const initPreloadListener = (sdkv2: SDKv2ServiceImpl) => {
ipcRenderer.on('bx-api-subscribe', async (event: Electron.Event, channel: SDKv2Selectors) => {
const senderId = getSenderId(event);
onFromWebContents('bx-api-subscribe', async (senderId: number, channel: SDKv2Selectors) => {
try {
const subscription = await sdkv2.addObserver(channel, observer({
on(result: any) {
// event.sender.send doesn't seem to work for renderer-to-renderer comms
ipcRenderer.sendTo(senderId, `bx-api-subscribe-response-${channel}`, result);
// renderer to renderer, relayed by the main process
sendToWebContents(senderId, `bx-api-subscribe-response-${channel}`, result);
},
}));

Expand All @@ -197,12 +194,11 @@ const initPreloadListener = (sdkv2: SDKv2ServiceImpl) => {
}
});

ipcRenderer.on('bx-api-perform', (event: Electron.Event, channel: SDKv2Selectors, payload?: any) => {
const senderId = getSenderId(event);
onFromWebContents('bx-api-perform', (senderId: number, channel: SDKv2Selectors, payload?: any) => {
sdkv2.callAction(channel, payload)
.then(result => {
// event.sender.send doesn't seem to work for renderer-to-renderer comms
ipcRenderer.sendTo(senderId, `bx-api-perform-response-${channel}`, result);
// renderer to renderer, relayed by the main process
sendToWebContents(senderId, `bx-api-perform-response-${channel}`, result);
})
.catch(handleError());
});
Expand Down
Loading
Loading