Merge branch 'develop' of github.com:vector-im/element-desktop into langleyd/fix_seshat_delete_contents

This commit is contained in:
David Langley 2024-10-16 21:50:33 +01:00
commit 58543536bc
33 changed files with 1460 additions and 1135 deletions

View File

@ -10,7 +10,7 @@ on:
jobs:
backport:
name: Backport
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
# Only react to merged PRs for security reasons.
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.
if: >

View File

@ -99,7 +99,7 @@ jobs:
- macos
- linux
- windows
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
name: ${{ needs.prepare.outputs.deploy == 'true' && 'Deploy' || 'Deploy (dry-run)' }}
if: always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')
environment: ${{ needs.prepare.outputs.deploy == 'true' && 'packages.element.io' || '' }}
@ -252,7 +252,7 @@ jobs:
deploy-ess:
needs: deploy
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
name: Deploy builds to ESS
if: needs.prepare.outputs.deploy == 'true' && github.event_name == 'release'
env:

View File

@ -60,12 +60,12 @@ jobs:
rsync -a /Volumes/Element/Element.app ~/Applications/ &&
hdiutil detach /Volumes/Element
- name: "Linux (amd64) (sqlcipher: system)"
os: ubuntu-latest
os: ubuntu-22.04
artifact: linux-amd64-sqlcipher-system
executable: "/opt/Element/element-desktop"
prepare_cmd: "sudo apt-get -qq update && sudo apt install ./dist/*.deb"
- name: "Linux (amd64) (sqlcipher: static)"
os: ubuntu-latest
os: ubuntu-22.04
artifact: linux-amd64-sqlcipher-static
executable: "/opt/Element/element-desktop"
prepare_cmd: "sudo apt-get -qq update && sudo apt install ./dist/*.deb"

View File

@ -26,7 +26,7 @@ jobs:
# We build the hak files on native infrastructure as matrix-seshat fails to cross-compile properly
# https://github.com/matrix-org/seshat/issues/135
hak:
runs-on: ${{ inputs.arch == 'arm64' && 'dind-l-arm64' || 'ubuntu-latest' }}
runs-on: ${{ inputs.arch == 'arm64' && 'dind-l-arm64' || 'ubuntu-22.04' }}
env:
HAK_DOCKER_IMAGE: ghcr.io/element-hq/element-desktop-dockerbuild
outputs:
@ -112,7 +112,7 @@ jobs:
- name: "Get modified files"
id: changed_files
if: steps.cache.outputs.cache-hit != 'true' && github.event_name == 'pull_request'
uses: tj-actions/changed-files@48d8f15b2aaa3d255ca5af3eba4870f807ce6b3c # v45
uses: tj-actions/changed-files@c3a1bb2c992d77180ae65be6ae6c166cf40f857c # v45
with:
files: |
dockerbuild/**
@ -148,7 +148,7 @@ jobs:
build:
needs: hak
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

View File

@ -45,7 +45,7 @@ jobs:
prepare:
name: Prepare
environment: ${{ inputs.nightly && 'packages.element.io' || '' }}
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
outputs:
nightly-version: ${{ steps.versions.outputs.nightly }}
steps:
@ -126,8 +126,7 @@ jobs:
BUNDLE_HASH=$(npx asar l webapp.asar | grep /bundles/ | head -n 1 | sed 's|.*/||')
WEBAPP_VERSION=$(./scripts/get-version.ts)
WEB_VERSION=${WEBAPP_VERSION:0:12}
REACT_VERSION=${WEBAPP_VERSION:19:12}
JS_VERSION=${WEBAPP_VERSION:35:12}
JS_VERSION=${WEBAPP_VERSION:16:12}
echo "### Nightly build ${{ steps.versions.outputs.nightly }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
@ -135,7 +134,6 @@ jobs:
echo "| ----------- | ------- |" >> $GITHUB_STEP_SUMMARY
echo "| Bundle Hash | $BUNDLE_HASH |" >> $GITHUB_STEP_SUMMARY
echo "| Element Web | [$WEB_VERSION](https://github.com/element-hq/element-web/commit/$WEB_VERSION) |" >> $GITHUB_STEP_SUMMARY
echo "| React SDK | [$REACT_VERSION](https://github.com/matrix-org/matrix-react-sdk/commit/$REACT_VERSION) |" >> $GITHUB_STEP_SUMMARY
echo "| JS SDK | [$JS_VERSION](https://github.com/matrix-org/matrix-js-sdk/commit/$JS_VERSION) |" >> $GITHUB_STEP_SUMMARY
- uses: actions/upload-artifact@v4

View File

@ -12,7 +12,7 @@ env:
jobs:
build:
name: Docker Build
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
@ -23,12 +23,12 @@ jobs:
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3
with:
install: true
- name: Log in to the Container registry
uses: docker/login-action@3b8fed7e4b60203b2aa0ecc6c6d6d91d12c06760
uses: docker/login-action@1f36f5b7a2d2f7bfd524795fc966e6d88c37baa9
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}

View File

@ -26,7 +26,7 @@ jobs:
check:
name: Post release checks
needs: release
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Wait for desktop packaging
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork

View File

@ -6,7 +6,7 @@ on:
jobs:
ts_lint:
name: "Typescript Syntax Check"
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
@ -30,7 +30,7 @@ jobs:
js_lint:
name: "ESLint"
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
@ -48,7 +48,7 @@ jobs:
workflow_lint:
name: "Workflow Lint"
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
@ -66,7 +66,7 @@ jobs:
analyse_dead_code:
name: "Analyse Dead Code"
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

View File

@ -6,7 +6,7 @@ on:
jobs:
automate-project-columns-next:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/add-to-project@main
with:

View File

@ -1,3 +1,7 @@
Changes in [1.11.81](https://github.com/element-hq/element-desktop/releases/tag/v1.11.81) (2024-10-15)
======================================================================================================
This release fixes High severity vulnerability CVE-2024-47771 / GHSA-963w-49j9-gxj6.
Changes in [1.11.80](https://github.com/element-hq/element-desktop/releases/tag/v1.11.80) (2024-10-08)
======================================================================================================
## ✨ Features

View File

@ -30,7 +30,11 @@ export default async function buildKeytar(hakEnv: HakEnv, moduleInfo: Dependency
},
);
proc.on("exit", (code) => {
code ? reject(code) : resolve();
if (code) {
reject(code);
} else {
resolve();
}
});
});
}

View File

@ -27,7 +27,11 @@ export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Prom
stdio: "inherit",
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
if (code) {
reject(code);
} else {
resolve();
}
});
});
@ -42,7 +46,11 @@ export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Prom
stdio: "inherit",
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
if (code) {
reject(code);
} else {
resolve();
}
});
});
}

View File

@ -2,7 +2,7 @@
"name": "element-desktop",
"productName": "Element",
"main": "lib/electron-main.js",
"version": "1.11.80",
"version": "1.11.81",
"description": "A feature-rich client for Matrix.org",
"author": "Element",
"homepage": "https://element.io",
@ -79,33 +79,34 @@
"@babel/core": "^7.18.10",
"@babel/preset-env": "^7.18.10",
"@babel/preset-typescript": "^7.18.6",
"@electron/asar": "^3.2.3",
"@electron/asar": "3.2.10",
"@electron/fuses": "^1.7.0",
"@mapbox/node-pre-gyp": "^1.0.11",
"@playwright/test": "1.47.1",
"@playwright/test": "1.48.0",
"@stylistic/eslint-plugin": "^2.9.0",
"@types/auto-launch": "^5.0.1",
"@types/counterpart": "^0.18.1",
"@types/minimist": "^1.2.1",
"@types/node": "18.19.54",
"@types/node": "18.19.55",
"@types/pacote": "^11.1.1",
"@types/tar": "^6.1.3",
"@types/uuid": "^10.0.0",
"@types/yargs": "^17.0.32",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"app-builder-lib": "24.13.3",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"app-builder-lib": "25.1.8",
"chokidar": "^4.0.0",
"detect-libc": "^2.0.0",
"electron": "^32.0.0",
"electron-builder": "24.13.3",
"electron-builder-squirrel-windows": "24.13.3",
"electron-builder": "25.1.8",
"electron-builder-squirrel-windows": "25.1.8",
"electron-devtools-installer": "^3.2.0",
"eslint": "^8.26.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-matrix-org": "^1.0.0",
"eslint-plugin-unicorn": "^55.0.0",
"eslint-plugin-matrix-org": "^2.0.1",
"eslint-plugin-unicorn": "^56.0.0",
"glob": "^11.0.0",
"knip": "^5.0.0",
"matrix-web-i18n": "^3.2.1",
@ -122,7 +123,7 @@
"keytar": "^7.9.0"
},
"resolutions": {
"@types/node": "18.19.54",
"@types/node": "18.19.55",
"config-file-ts": "0.2.8-rc1"
}
}

View File

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/playwright:v1.46.1-jammy
FROM mcr.microsoft.com/playwright:v1.48.1-jammy
WORKDIR /work/element-desktop

BIN
res/img/monochrome.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
res/img/monochrome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -28,7 +28,7 @@ async function downloadToFile(url: string, filename: string): Promise<void> {
console.error(e);
try {
await fs.unlink(filename);
} catch (_) {}
} catch {}
throw e;
}
}
@ -150,14 +150,14 @@ async function main(): Promise<number | undefined> {
await fs.opendir(expectedDeployDir);
console.log(expectedDeployDir + "already exists");
haveDeploy = true;
} catch (e) {}
} catch {}
if (!haveDeploy) {
const outPath = path.join(pkgDir, filename);
try {
await fs.stat(outPath);
console.log("Already have " + filename + ": not redownloading");
} catch (e) {
} catch {
try {
await downloadToFile(url, outPath);
} catch (e) {
@ -170,7 +170,7 @@ async function main(): Promise<number | undefined> {
try {
await fs.stat(outPath + ".asc");
console.log("Already have " + filename + ".asc: not redownloading");
} catch (e) {
} catch {
try {
await downloadToFile(url + ".asc", outPath + ".asc");
} catch (e) {
@ -206,7 +206,7 @@ async function main(): Promise<number | undefined> {
await fs.stat(ASAR_PATH);
console.log(ASAR_PATH + " already present: removing");
await fs.unlink(ASAR_PATH);
} catch (e) {}
} catch {}
if (cfgDir.length) {
const configJsonSource = path.join(cfgDir, "config.json");

View File

@ -18,7 +18,7 @@ export default async function fetch(hakEnv: HakEnv, moduleInfo: DependencyInfo):
try {
const stats = await fsProm.stat(moduleInfo.moduleBuildDir);
haveModuleBuildDir = stats.isDirectory();
} catch (e) {
} catch {
haveModuleBuildDir = false;
}
@ -41,7 +41,11 @@ export default async function fetch(hakEnv: HakEnv, moduleInfo: DependencyInfo):
shell: hakEnv.isWin(),
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
if (code) {
reject(code);
} else {
resolve();
}
});
});

View File

@ -31,8 +31,9 @@ async function main(): Promise<void> {
const prefix = path.join(__dirname, "..", "..");
let packageJson;
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
packageJson = require(path.join(prefix, "package.json"));
} catch (e) {
} catch {
console.error("Can't find a package.json!");
process.exit(1);
}
@ -69,8 +70,9 @@ async function main(): Promise<void> {
const hakJsonPath = path.join(prefix, "hak", dep, "hak.json");
let hakJson: Record<string, any>;
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
hakJson = await require(hakJsonPath);
} catch (e) {
} catch {
console.error("No hak.json found for " + dep + ".");
console.log("Expecting " + hakJsonPath);
process.exit(1);

View File

@ -24,7 +24,7 @@ export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo):
// Also we do this for each module which is unnecessary, but meh.
try {
await fsProm.stat(yarnrc);
} catch (e) {
} catch {
await fsProm.writeFile(
yarnrc,
// XXX: 1. This must be absolute, as yarn will resolve link directories
@ -50,7 +50,11 @@ export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo):
shell: hakEnv.isWin(),
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
if (code) {
reject(code);
} else {
resolve();
}
});
});
@ -63,7 +67,11 @@ export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo):
shell: hakEnv.isWin(),
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
if (code) {
reject(code);
} else {
resolve();
}
});
});
}

View File

@ -12,7 +12,7 @@ import * as childProcess from "child_process";
export async function versionFromAsar(): Promise<string> {
try {
await fs.stat("webapp.asar");
} catch (e) {
} catch {
throw new Error("No 'webapp.asar' found. Run 'yarn run fetch'");
}

View File

@ -20,8 +20,8 @@ declare global {
var launcher: AutoLaunch;
var vectorConfig: Record<string, any>;
var trayConfig: {
// eslint-disable-next-line camelcase
icon_path: string;
color_icon_path: string; // eslint-disable-line camelcase
monochrome_icon_path: string; // eslint-disable-line camelcase
brand: string;
};
var store: Store<{
@ -31,6 +31,7 @@ declare global {
autoHideMenuBar?: boolean;
locale?: string | string[];
disableHardwareAcceleration?: boolean;
monochromeIcon?: boolean;
}>;
}
/* eslint-enable no-var */

View File

@ -97,7 +97,7 @@ async function tryPaths(name: string, root: string, rawPaths: string[]): Promise
try {
await afs.stat(p);
return p + "/";
} catch (e) {}
} catch {}
}
console.log(`Couldn't find ${name} files in any of: `);
for (const p of paths) {
@ -137,7 +137,7 @@ async function loadConfig(): Promise<void> {
try {
global.vectorConfig = loadJsonFile(asarPath, "config.json");
} catch (e) {
} catch {
// it would be nice to check the error code here and bail if the config
// is unparsable, but we get MODULE_NOT_FOUND in the case of a missing
// file or invalid json, so node is just very unhelpful.
@ -212,9 +212,11 @@ async function setupGlobals(): Promise<void> {
// The tray icon
// It's important to call `path.join` so we don't end up with the packaged asar in the final path.
const iconFile = `element.${process.platform === "win32" ? "ico" : "png"}`;
const colorIconFile = `element.${process.platform === "win32" ? "ico" : "png"}`;
const monochromeIconFile = `monochrome.${process.platform === "win32" ? "ico" : "png"}`;
global.trayConfig = {
icon_path: path.join(resPath, "img", iconFile),
monochrome_icon_path: path.join(resPath, "img", monochromeIconFile),
color_icon_path: path.join(resPath, "img", colorIconFile),
brand: global.vectorConfig.brand || "Element",
};
@ -365,7 +367,7 @@ app.on("ready", async () => {
if (argv["devtools"]) {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { default: installExt, REACT_DEVELOPER_TOOLS, REACT_PERF } = require("electron-devtools-installer");
installExt(REACT_DEVELOPER_TOOLS)
.then((name: string) => console.log(`Added Extension: ${name}`))
@ -453,7 +455,7 @@ app.on("ready", async () => {
titleBarStyle: process.platform === "darwin" ? "hidden" : "default",
trafficLightPosition: { x: 9, y: 8 },
icon: global.trayConfig.icon_path,
icon: global.trayConfig.color_icon_path,
show: false,
autoHideMenuBar: global.store.get("autoHideMenuBar", true),

View File

@ -147,7 +147,7 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
if (ret === null) {
ret = await keytar?.getPassword("riot.im", `${args[0]}|${args[1]}`);
}
} catch (e) {
} catch {
// if an error is thrown (e.g. keytar can't connect to the keychain),
// then return null, which means the default pickle key will be used
ret = null;
@ -159,7 +159,7 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
const pickleKey = await randomArray(32);
await keytar?.setPassword("element.io", `${args[0]}|${args[1]}`, pickleKey);
ret = pickleKey;
} catch (e) {
} catch {
ret = null;
}
break;
@ -170,7 +170,7 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
// migrate from riot.im (remove once we think there will no longer be
// logins from the time of riot.im)
await keytar?.deletePassword("riot.im", `${args[0]}|${args[1]}`);
} catch (e) {}
} catch {}
break;
case "getDesktopCapturerSources":
ret = (await desktopCapturer.getSources(args[0])).map((source) => ({

View File

@ -9,7 +9,7 @@ import type * as Keytar from "keytar"; // Hak dependency type
let keytar: typeof Keytar | undefined;
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
// eslint-disable-next-line @typescript-eslint/no-require-imports
keytar = require("keytar");
} catch (e) {
if ((<NodeJS.ErrnoException>e).code === "MODULE_NOT_FOUND") {

View File

@ -33,39 +33,74 @@ async function getAccessToken(window: BrowserWindow): Promise<string | undefined
});
}
/**
* Get the homeserver url
* This requires asking the renderer process for the homeserver url.
*/
async function getHomeserverUrl(window: BrowserWindow): Promise<string> {
return new Promise((resolve) => {
ipcMain.once("homeserverUrl", (_, homeserver) => {
resolve(homeserver);
});
window.webContents.send("homeserverUrl"); // ping now that the listener exists
});
}
export function setupMediaAuth(window: BrowserWindow): void {
session.defaultSession.webRequest.onBeforeRequest(async (req, callback) => {
// This handler emulates the element-web service worker, where URLs are rewritten late in the request
// for backwards compatibility. As authenticated media becomes more prevalent, this should be replaced
// by the app using authenticated URLs from the outset.
let url = req.url;
if (!url.includes("/_matrix/media/v3/download") && !url.includes("/_matrix/media/v3/thumbnail")) {
return callback({}); // not a URL we care about
}
try {
const url = new URL(req.url);
if (
!url.pathname.startsWith("/_matrix/media/v3/download") &&
!url.pathname.startsWith("/_matrix/media/v3/thumbnail")
) {
return callback({}); // not a URL we care about
}
const supportedVersions = await getSupportedVersions(window);
// We have to check that the access token is truthy otherwise we'd be intercepting pre-login media request too,
// e.g. those required for SSO button icons.
const accessToken = await getAccessToken(window);
if (supportedVersions.includes("v1.11") && accessToken) {
url = url.replace(/\/media\/v3\/(.*)\//, "/client/v1/media/$1/");
return callback({ redirectURL: url });
} else {
return callback({}); // no support == no modification
const supportedVersions = await getSupportedVersions(window);
// We have to check that the access token is truthy otherwise we'd be intercepting pre-login media request too,
// e.g. those required for SSO button icons.
const accessToken = await getAccessToken(window);
if (supportedVersions.includes("v1.11") && accessToken) {
url.href = url.href.replace(/\/media\/v3\/(.*)\//, "/client/v1/media/$1/");
return callback({ redirectURL: url.toString() });
} else {
return callback({}); // no support == no modification
}
} catch (e) {
console.error(e);
}
});
session.defaultSession.webRequest.onBeforeSendHeaders(async (req, callback) => {
if (!req.url.includes("/_matrix/client/v1/media")) {
return callback({}); // invoke unmodified
}
try {
const url = new URL(req.url);
if (!url.pathname.startsWith("/_matrix/client/v1/media")) {
return callback({}); // invoke unmodified
}
// Only add authorization header to authenticated media URLs. This emulates the service worker
// behaviour in element-web.
const accessToken = await getAccessToken(window);
// `accessToken` can be falsy, but if we're trying to download media without authentication
// then we should expect failure anyway.
const headers = { ...req.requestHeaders, Authorization: `Bearer ${accessToken}` };
return callback({ requestHeaders: headers });
// Is this request actually going to the homeserver?
// We don't combine this check with the one above on purpose.
// We're fetching the homeserver url through IPC and should do so
// as sparingly as possible.
const homeserver = await getHomeserverUrl(window);
const isRequestToHomeServer = homeserver && url.origin === new URL(homeserver).origin;
if (!isRequestToHomeServer) {
return callback({}); // invoke unmodified
}
// Only add authorization header to authenticated media URLs. This emulates the service worker
// behaviour in element-web.
const accessToken = await getAccessToken(window);
// `accessToken` can be falsy, but if we're trying to download media without authentication
// then we should expect failure anyway.
const headers = { ...req.requestHeaders, Authorization: `Bearer ${accessToken}` };
return callback({ requestHeaders: headers });
} catch (e) {
console.error(e);
}
});
}

View File

@ -28,6 +28,7 @@ const CHANNELS = [
"userDownloadAction",
"openDesktopCapturerSourcePicker",
"userAccessToken",
"homeserverUrl",
"serverSupportedVersions",
];

View File

@ -50,7 +50,7 @@ function readStore(): Record<string, string> {
const s = fs.readFileSync(storePath, { encoding: "utf8" });
const o = JSON.parse(s);
return typeof o === "object" ? o : {};
} catch (e) {
} catch {
return {};
}
}

View File

@ -24,7 +24,7 @@ let SeshatRecovery: typeof SeshatRecoveryType;
let ReindexError: typeof ReindexErrorType;
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
// eslint-disable-next-line @typescript-eslint/no-require-imports
const seshatModule = require("matrix-seshat");
Seshat = seshatModule.Seshat;
SeshatRecovery = seshatModule.SeshatRecovery;
@ -267,7 +267,7 @@ ipcMain.on("seshat", async function (_ev: IpcMainEvent, payload): Promise<void>
else {
try {
ret = await eventIndex.loadCheckpoints();
} catch (e) {
} catch {
ret = [];
}
}

View File

@ -67,4 +67,13 @@ export const Settings: Record<string, Setting> = {
global.store.set("disableHardwareAcceleration", !value);
},
},
"Electron.monochromeIcon": {
async read(): Promise<any> {
return tray.isMonochrome();
},
async write(value: any): Promise<void> {
global.store.set("monochromeIcon", value);
tray.refreshIcon();
},
},
};

View File

@ -28,6 +28,19 @@ export function destroy(): void {
}
}
export function isMonochrome(): boolean {
return global.store.get("monochromeIcon", process.platform === "linux");
}
export function refreshIcon(): void {
const monochrome = isMonochrome();
if (monochrome) {
trayIcon?.setImage(nativeImage.createFromPath(global.trayConfig.monochrome_icon_path));
} else {
trayIcon?.setImage(nativeImage.createFromPath(global.trayConfig.color_icon_path));
}
}
function toggleWin(): void {
if (global.mainWindow?.isVisible() && !global.mainWindow.isMinimized() && global.mainWindow.isFocused()) {
global.mainWindow.hide();
@ -39,7 +52,8 @@ function toggleWin(): void {
}
interface IConfig {
icon_path: string; // eslint-disable-line camelcase
color_icon_path: string; // eslint-disable-line camelcase
monochrome_icon_path: string; // eslint-disable-line camelcase
brand: string;
}
@ -52,7 +66,9 @@ function getUuid(): string {
export function create(config: IConfig): void {
// no trays on darwin
if (process.platform === "darwin" || trayIcon) return;
const defaultIcon = nativeImage.createFromPath(config.icon_path);
const defaultIcon = nativeImage.createFromPath(
isMonochrome() ? config.monochrome_icon_path : config.color_icon_path,
);
let guid: string | undefined;
if (process.platform === "win32" && app.isPackaged) {

View File

@ -176,15 +176,18 @@ function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: We
ev.preventDefault();
}
function cutCopyPasteSelectContextMenus(params: ContextMenuParams): MenuItemConstructorOptions[] {
function cutCopyPasteSelectContextMenus(
params: ContextMenuParams,
webContents: WebContents,
): MenuItemConstructorOptions[] {
const options: MenuItemConstructorOptions[] = [];
if (params.misspelledWord) {
params.dictionarySuggestions.forEach((word) => {
options.push({
label: word,
click: (menuItem, browserWindow) => {
browserWindow?.webContents.replaceMisspelling(word);
click: () => {
webContents.replaceMisspelling(word);
},
});
});
@ -194,8 +197,8 @@ function cutCopyPasteSelectContextMenus(params: ContextMenuParams): MenuItemCons
},
{
label: _t("right_click_menu|add_to_dictionary"),
click: (menuItem, browserWindow) => {
browserWindow?.webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord);
click: () => {
webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord);
},
},
{
@ -237,8 +240,8 @@ function cutCopyPasteSelectContextMenus(params: ContextMenuParams): MenuItemCons
return options;
}
function onSelectedContextMenu(ev: Event, params: ContextMenuParams): void {
const items = cutCopyPasteSelectContextMenus(params);
function onSelectedContextMenu(ev: Event, params: ContextMenuParams, webContents: WebContents): void {
const items = cutCopyPasteSelectContextMenus(params, webContents);
const popupMenu = Menu.buildFromTemplate(items);
// popup() requires an options object even for no options
@ -246,12 +249,12 @@ function onSelectedContextMenu(ev: Event, params: ContextMenuParams): void {
ev.preventDefault();
}
function onEditableContextMenu(ev: Event, params: ContextMenuParams): void {
function onEditableContextMenu(ev: Event, params: ContextMenuParams, webContents: WebContents): void {
const items: MenuItemConstructorOptions[] = [
{ role: "undo" },
{ role: "redo", enabled: params.editFlags.canRedo },
{ type: "separator" },
...cutCopyPasteSelectContextMenus(params),
...cutCopyPasteSelectContextMenus(params, webContents),
];
const popupMenu = Menu.buildFromTemplate(items);
@ -286,9 +289,9 @@ export default (webContents: WebContents): void => {
if (params.linkURL || params.srcURL) {
onLinkContextMenu(ev, params, webContents);
} else if (params.selectionText) {
onSelectedContextMenu(ev, params);
onSelectedContextMenu(ev, params, webContents);
} else if (params.isEditable) {
onEditableContextMenu(ev, params);
onEditableContextMenu(ev, params, webContents);
}
});

2297
yarn.lock

File diff suppressed because it is too large Load Diff