mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
desktop: add support for switching release tracks
this also includes relevant tests for the auto updater
This commit is contained in:
committed by
Abdullah Atta
parent
725f3c3522
commit
15f4f2accb
2
apps/desktop/.gitignore
vendored
2
apps/desktop/.gitignore
vendored
@@ -3,3 +3,5 @@ build
|
|||||||
output
|
output
|
||||||
dist
|
dist
|
||||||
_catalog
|
_catalog
|
||||||
|
test-results
|
||||||
|
test-artifacts
|
||||||
|
|||||||
119
apps/desktop/__tests__/auto-updates.test.ts
Normal file
119
apps/desktop/__tests__/auto-updates.test.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the Notesnook project (https://notesnook.com/)
|
||||||
|
|
||||||
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { test } from "vitest";
|
||||||
|
import { harness } from "./utils.js";
|
||||||
|
import assert from "assert";
|
||||||
|
import { writeFile } from "fs/promises";
|
||||||
|
|
||||||
|
test("update starts downloading if version is outdated", async (t) => {
|
||||||
|
await harness(
|
||||||
|
t,
|
||||||
|
async ({ page }) => {
|
||||||
|
await page.waitForSelector("#authForm");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
await page.getByRole("button", { name: "Create account" }).isVisible()
|
||||||
|
);
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole("button", { name: "Skip & go directly to the app" })
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.waitForSelector(".ReactModal__Content", {
|
||||||
|
timeout: 1000
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.then(async () => {
|
||||||
|
const positiveButton = page.locator(
|
||||||
|
"button[data-role='positive-button']"
|
||||||
|
);
|
||||||
|
const negativeButton = page.locator(
|
||||||
|
"button[data-role='negative-button']"
|
||||||
|
);
|
||||||
|
if (await positiveButton.isVisible()) await positiveButton.click();
|
||||||
|
else if (await negativeButton.isVisible())
|
||||||
|
await negativeButton.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForSelector(".ProseMirror");
|
||||||
|
|
||||||
|
await page
|
||||||
|
.locator(".theme-scope-statusBar")
|
||||||
|
.getByRole("button", { name: /updating/i })
|
||||||
|
.waitFor({ state: "attached" });
|
||||||
|
},
|
||||||
|
{ version: "3.0.0" }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("update is only shown if version is outdated and auto updates are disabled", async (t) => {
|
||||||
|
await harness(
|
||||||
|
t,
|
||||||
|
async (ctx) => {
|
||||||
|
await ctx.app.close();
|
||||||
|
await writeFile(
|
||||||
|
ctx.configPath,
|
||||||
|
JSON.stringify({
|
||||||
|
automaticUpdates: false
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await ctx.relaunch();
|
||||||
|
|
||||||
|
const { page } = ctx;
|
||||||
|
|
||||||
|
await page.waitForSelector("#authForm");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
await page.getByRole("button", { name: "Create account" }).isVisible()
|
||||||
|
);
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole("button", { name: "Skip & go directly to the app" })
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.waitForSelector(".ReactModal__Content", {
|
||||||
|
timeout: 1000
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.then(async () => {
|
||||||
|
const positiveButton = page.locator(
|
||||||
|
"button[data-role='positive-button']"
|
||||||
|
);
|
||||||
|
const negativeButton = page.locator(
|
||||||
|
"button[data-role='negative-button']"
|
||||||
|
);
|
||||||
|
if (await positiveButton.isVisible()) await positiveButton.click();
|
||||||
|
else if (await negativeButton.isVisible())
|
||||||
|
await negativeButton.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForSelector(".ProseMirror");
|
||||||
|
|
||||||
|
await page
|
||||||
|
.locator(".theme-scope-statusBar")
|
||||||
|
.getByRole("button", { name: /available/i })
|
||||||
|
.waitFor({ state: "attached" });
|
||||||
|
},
|
||||||
|
{ version: "3.0.0" }
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -17,16 +17,12 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import test from "node:test";
|
import { test } from "vitest";
|
||||||
import { launchApp } from "./utils.mjs";
|
import { harness } from "./utils.js";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import slugify from "slugify";
|
|
||||||
import path from "path";
|
|
||||||
import { mkdir } from "fs/promises";
|
|
||||||
|
|
||||||
test("make sure app loads", async (t) => {
|
test("make sure app loads", async (t) => {
|
||||||
const { app, page } = await launchApp();
|
await harness(t, async ({ page }) => {
|
||||||
try {
|
|
||||||
await page.waitForSelector("#authForm");
|
await page.waitForSelector("#authForm");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
@@ -38,16 +34,5 @@ test("make sure app loads", async (t) => {
|
|||||||
.click();
|
.click();
|
||||||
|
|
||||||
await page.waitForSelector(".ProseMirror");
|
await page.waitForSelector(".ProseMirror");
|
||||||
} catch (e) {
|
|
||||||
await mkdir("test-results", { recursive: true });
|
|
||||||
await page.screenshot({
|
|
||||||
path: path.join(
|
|
||||||
"test-results",
|
|
||||||
`${slugify(t.name)}-${process.platform}-${process.arch}-error.png`
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
await app.close();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
158
apps/desktop/__tests__/utils.ts
Normal file
158
apps/desktop/__tests__/utils.ts
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the Notesnook project (https://notesnook.com/)
|
||||||
|
|
||||||
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { execSync } from "child_process";
|
||||||
|
import { mkdir } from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
import { _electron as electron } from "playwright";
|
||||||
|
import slugify from "slugify";
|
||||||
|
import { TaskContext } from "vitest";
|
||||||
|
|
||||||
|
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
||||||
|
|
||||||
|
interface AppContext {
|
||||||
|
app: import("playwright").ElectronApplication;
|
||||||
|
page: import("playwright").Page;
|
||||||
|
configPath: string;
|
||||||
|
relaunch: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TestOptions {
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function harness(
|
||||||
|
t: TaskContext,
|
||||||
|
cb: (ctx: AppContext) => Promise<void>,
|
||||||
|
options?: TestOptions
|
||||||
|
) {
|
||||||
|
const ctx = await buildAndLaunchApp(options);
|
||||||
|
|
||||||
|
t.onTestFinished(async (result) => {
|
||||||
|
if (result.state === "fail") {
|
||||||
|
await mkdir("test-results", { recursive: true });
|
||||||
|
await ctx.page.screenshot({
|
||||||
|
path: path.join(
|
||||||
|
"test-results",
|
||||||
|
`${slugify(t.task.name)}-${process.platform}-${
|
||||||
|
process.arch
|
||||||
|
}-error.png`
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await ctx.app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
await cb(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildAndLaunchApp(options?: TestOptions): Promise<AppContext> {
|
||||||
|
const productName = makeid(10);
|
||||||
|
const executablePath = await buildApp({ ...options, productName });
|
||||||
|
const { app, page, configPath } = await launchApp(executablePath);
|
||||||
|
const ctx: AppContext = {
|
||||||
|
app,
|
||||||
|
page,
|
||||||
|
configPath,
|
||||||
|
relaunch: async () => {
|
||||||
|
const { app, page, configPath } = await launchApp(executablePath);
|
||||||
|
ctx.app = app;
|
||||||
|
ctx.page = page;
|
||||||
|
ctx.configPath = configPath;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function launchApp(executablePath: string) {
|
||||||
|
const app = await electron.launch({
|
||||||
|
executablePath,
|
||||||
|
args: process.env.NN_DEBUG ? [] : ["--hidden"]
|
||||||
|
});
|
||||||
|
|
||||||
|
const page = await app.firstWindow();
|
||||||
|
|
||||||
|
const userDataDirectory = await app.evaluate((a) => {
|
||||||
|
return a.app.getPath("userData");
|
||||||
|
});
|
||||||
|
const configPath = path.join(userDataDirectory, "config.json");
|
||||||
|
return {
|
||||||
|
app,
|
||||||
|
page,
|
||||||
|
configPath
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildApp({
|
||||||
|
version,
|
||||||
|
productName
|
||||||
|
}: {
|
||||||
|
version?: string;
|
||||||
|
productName: string;
|
||||||
|
}) {
|
||||||
|
const buildRoot = path.join("test-artifacts", `${productName}-build`);
|
||||||
|
const output = path.join("test-artifacts", `${productName}-output`);
|
||||||
|
execSync(`npm run release -- --root ${buildRoot}`, {
|
||||||
|
stdio: process.env.NN_DEBUG ? "inherit" : "ignore"
|
||||||
|
});
|
||||||
|
|
||||||
|
const args = [
|
||||||
|
`--config electron-builder.config.js`,
|
||||||
|
`--c.extraMetadata.productName=${productName}`
|
||||||
|
];
|
||||||
|
if (version) args.push(`--c.extraMetadata.version=${version}`);
|
||||||
|
|
||||||
|
execSync(`npx electron-builder --dir --${process.arch} ${args.join(" ")}`, {
|
||||||
|
stdio: process.env.NN_DEBUG ? "inherit" : "ignore",
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
NN_BUILD_ROOT: buildRoot,
|
||||||
|
NN_PRODUCT_NAME: productName,
|
||||||
|
NN_APP_ID: `com.notesnook.test.${productName}`,
|
||||||
|
NN_OUTPUT_DIR: output
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
output,
|
||||||
|
process.platform === "linux"
|
||||||
|
? "linux-unpacked"
|
||||||
|
: process.platform === "darwin"
|
||||||
|
? process.arch === "arm64"
|
||||||
|
? `mac-arm64/${productName}.app/Contents/MacOS/`
|
||||||
|
: `mac/${productName}.app/Contents/MacOS/`
|
||||||
|
: "win-unpacked",
|
||||||
|
process.platform === "win32" ? `${productName}.exe` : productName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeid(length: number) {
|
||||||
|
let result = "";
|
||||||
|
const characters =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
const charactersLength = characters.length;
|
||||||
|
let counter = 0;
|
||||||
|
while (counter < length) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
197
apps/desktop/electron-builder.config.js
Normal file
197
apps/desktop/electron-builder.config.js
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the Notesnook project (https://notesnook.com/)
|
||||||
|
|
||||||
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const buildRoot = process.env.NN_BUILD_ROOT || ".";
|
||||||
|
const buildFiles = [
|
||||||
|
`${buildRoot}/build/`,
|
||||||
|
`!${buildRoot}/build/screenshots\${/*}`,
|
||||||
|
`!${buildRoot}/build/banner.jpg`,
|
||||||
|
`!${buildRoot}/build/*.ico`,
|
||||||
|
`!${buildRoot}/build/*.png`
|
||||||
|
];
|
||||||
|
|
||||||
|
const productName = process.env.NN_PRODUCT_NAME || "Notesnook";
|
||||||
|
const appId = process.env.NN_APP_ID || "org.streetwriters.notesnook";
|
||||||
|
const outputDir = process.env.NN_OUTPUT_DIR || "output";
|
||||||
|
const year = new Date().getFullYear();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
appId: appId,
|
||||||
|
productName: productName,
|
||||||
|
copyright: `Copyright © ${year} Streetwriters (Private) Limited`,
|
||||||
|
artifactName: "notesnook_${os}_${arch}.${ext}",
|
||||||
|
generateUpdatesFilesForAllChannels: true,
|
||||||
|
asar: false,
|
||||||
|
files: [
|
||||||
|
"!*.chunk.js.map",
|
||||||
|
"!*.chunk.js.LICENSE.txt",
|
||||||
|
...buildFiles,
|
||||||
|
"!node_modules${/*}",
|
||||||
|
"node_modules/better-sqlite3-multiple-ciphers/build/Release/better_sqlite3.node",
|
||||||
|
"node_modules/better-sqlite3-multiple-ciphers/lib",
|
||||||
|
"node_modules/better-sqlite3-multiple-ciphers/package.json",
|
||||||
|
"node_modules/file-uri-to-path",
|
||||||
|
"node_modules/bindings",
|
||||||
|
"node_modules/node-gyp-build",
|
||||||
|
"node_modules/sqlite-better-trigram",
|
||||||
|
"node_modules/sodium-native/prebuilds/${platform}-${arch}",
|
||||||
|
{
|
||||||
|
from: "node_modules/sqlite-better-trigram-linux-${arch}",
|
||||||
|
to: "node_modules/sqlite-better-trigram-linux-${arch}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "node_modules/sqlite-better-trigram-darwin-${arch}",
|
||||||
|
to: "node_modules/sqlite-better-trigram-darwin-${arch}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "node_modules/sqlite-better-trigram-windows-${arch}",
|
||||||
|
to: "node_modules/sqlite-better-trigram-windows-${arch}"
|
||||||
|
},
|
||||||
|
"node_modules/sodium-native/index.js",
|
||||||
|
"node_modules/sodium-native/package.json"
|
||||||
|
],
|
||||||
|
afterPack: "./scripts/removeLocales.js",
|
||||||
|
mac: {
|
||||||
|
bundleVersion: "240",
|
||||||
|
minimumSystemVersion: "10.12.0",
|
||||||
|
target: [
|
||||||
|
{
|
||||||
|
target: "dmg",
|
||||||
|
arch: ["arm64", "x64"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "zip",
|
||||||
|
arch: ["arm64", "x64"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
category: "public.app-category.productivity",
|
||||||
|
darkModeSupport: true,
|
||||||
|
type: "distribution",
|
||||||
|
hardenedRuntime: true,
|
||||||
|
entitlements: "assets/entitlements.mac.plist",
|
||||||
|
entitlementsInherit: "assets/entitlements.mac.plist",
|
||||||
|
gatekeeperAssess: false,
|
||||||
|
icon: "assets/icons/app.icns",
|
||||||
|
notarize: true
|
||||||
|
},
|
||||||
|
dmg: {
|
||||||
|
contents: [
|
||||||
|
{
|
||||||
|
x: 130,
|
||||||
|
y: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 410,
|
||||||
|
y: 220,
|
||||||
|
type: "link",
|
||||||
|
path: "/Applications"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
icon: "assets/icons/app.icns",
|
||||||
|
title: "Install Notesnook"
|
||||||
|
},
|
||||||
|
mas: {
|
||||||
|
entitlements: "assets/entitlements.mas.plist",
|
||||||
|
entitlementsInherit: "assets/entitlements.mas.inherit.plist",
|
||||||
|
entitlementsLoginHelper: "assets/entitlements.mas.loginhelper.plist",
|
||||||
|
hardenedRuntime: true
|
||||||
|
},
|
||||||
|
win: {
|
||||||
|
target: [
|
||||||
|
{
|
||||||
|
target: "nsis",
|
||||||
|
arch: ["x64", "arm64"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "portable",
|
||||||
|
arch: ["x64", "arm64"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
signtoolOptions: {
|
||||||
|
signingHashAlgorithms: ["sha256"],
|
||||||
|
sign: "./scripts/sign.js"
|
||||||
|
},
|
||||||
|
icon: "assets/icons/app.ico"
|
||||||
|
},
|
||||||
|
portable: {
|
||||||
|
artifactName: "notesnook_${os}_${arch}_portable.${ext}"
|
||||||
|
},
|
||||||
|
nsis: {
|
||||||
|
oneClick: true,
|
||||||
|
createDesktopShortcut: "always",
|
||||||
|
deleteAppDataOnUninstall: true
|
||||||
|
},
|
||||||
|
linux: {
|
||||||
|
target: [
|
||||||
|
{
|
||||||
|
target: "AppImage",
|
||||||
|
arch: ["x64", "arm64"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "snap",
|
||||||
|
arch: ["x64", "arm64"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
category: "Office",
|
||||||
|
icon: "assets/icons/app.icns",
|
||||||
|
description: "Your private note taking space",
|
||||||
|
executableName: "notesnook",
|
||||||
|
desktop: {
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
id: "new-note",
|
||||||
|
name: "New note",
|
||||||
|
args: "new note"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "new-notebook",
|
||||||
|
name: "New notebook",
|
||||||
|
args: "new notebook"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "new-reminder",
|
||||||
|
name: "New reminder",
|
||||||
|
args: "new reminder"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
snap: {
|
||||||
|
autoStart: false,
|
||||||
|
confinement: "strict",
|
||||||
|
allowNativeWayland: true
|
||||||
|
},
|
||||||
|
extraResources: ["app-update.yml", "./assets/**"],
|
||||||
|
extraMetadata: {
|
||||||
|
main: path.join(buildRoot, "build", "electron.js")
|
||||||
|
},
|
||||||
|
directories: {
|
||||||
|
buildResources: "assets",
|
||||||
|
output: outputDir
|
||||||
|
},
|
||||||
|
publish: [
|
||||||
|
{
|
||||||
|
provider: "github",
|
||||||
|
repo: "notesnook",
|
||||||
|
owner: "streetwriters"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
753
apps/desktop/package-lock.json
generated
753
apps/desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@
|
|||||||
"@trpc/client": "10.45.2",
|
"@trpc/client": "10.45.2",
|
||||||
"@trpc/server": "10.45.2",
|
"@trpc/server": "10.45.2",
|
||||||
"better-sqlite3-multiple-ciphers": "11.5.0",
|
"better-sqlite3-multiple-ciphers": "11.5.0",
|
||||||
"electron-trpc": "0.6.1",
|
"electron-trpc": "0.7.1",
|
||||||
"electron-updater": "^6.3.4",
|
"electron-updater": "^6.3.4",
|
||||||
"icojs": "^0.19.4",
|
"icojs": "^0.19.4",
|
||||||
"sqlite-better-trigram": "0.0.2",
|
"sqlite-better-trigram": "0.0.2",
|
||||||
@@ -46,14 +46,14 @@
|
|||||||
"electron": "^31.7.4",
|
"electron": "^31.7.4",
|
||||||
"electron-builder": "^25.1.8",
|
"electron-builder": "^25.1.8",
|
||||||
"esbuild": "0.21.5",
|
"esbuild": "0.21.5",
|
||||||
"vitest": "2.1.8",
|
|
||||||
"node-abi": "^3.68.0",
|
"node-abi": "^3.68.0",
|
||||||
"node-gyp-build": "^4.8.2",
|
"node-gyp-build": "^4.8.2",
|
||||||
"playwright": "^1.48.2",
|
"playwright": "^1.48.2",
|
||||||
"prebuildify": "^6.0.1",
|
"prebuildify": "^6.0.1",
|
||||||
"slugify": "1.6.6",
|
"slugify": "1.6.6",
|
||||||
"tree-kill": "^1.2.2",
|
"tree-kill": "^1.2.2",
|
||||||
"undici": "^6.19.8"
|
"undici": "^6.19.8",
|
||||||
|
"vitest": "^2.1.8"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"dmg-license": "^1.0.11"
|
"dmg-license": "^1.0.11"
|
||||||
@@ -63,203 +63,14 @@
|
|||||||
"staging": "node scripts/build.mjs --run",
|
"staging": "node scripts/build.mjs --run",
|
||||||
"release": "node scripts/build.mjs",
|
"release": "node scripts/build.mjs",
|
||||||
"build": "node ../../scripts/build.mjs",
|
"build": "node ../../scripts/build.mjs",
|
||||||
"bundle": "esbuild electron=./src/main.ts ./src/preload.ts --external:electron --external:fsevents --external:better-sqlite3-multiple-ciphers --external:sodium-native --minify --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=false --define:RELEASE=true",
|
"bundle": "esbuild electron=./src/main.ts ./src/preload.ts --external:electron --external:fsevents --external:better-sqlite3-multiple-ciphers --external:sodium-native --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=false --define:RELEASE=true",
|
||||||
"bundle:mas": "esbuild electron=./src/main.ts ./src/preload.ts --minify --external:electron --external:fsevents --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=true --define:RELEASE=true",
|
"bundle:mas": "esbuild electron=./src/main.ts ./src/preload.ts --minify --external:electron --external:fsevents --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=true --define:RELEASE=true",
|
||||||
"postinstall": "patch-package",
|
"postinstall": "patch-package",
|
||||||
"test": "node --test --test-timeout 30000 --test-force-exit"
|
"test": "node --test --test-timeout 30000 --test-force-exit --test-concurrency=8"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Streetwriters (Private) Limited",
|
"name": "Streetwriters (Private) Limited",
|
||||||
"email": "support@streetwriters.co",
|
"email": "support@streetwriters.co",
|
||||||
"url": "https://streetwriters.co"
|
"url": "https://streetwriters.co"
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"appId": "org.streetwriters.notesnook",
|
|
||||||
"productName": "Notesnook",
|
|
||||||
"copyright": "Copyright © 2023 Streetwriters (Private) Limited",
|
|
||||||
"artifactName": "notesnook_${os}_${arch}.${ext}",
|
|
||||||
"generateUpdatesFilesForAllChannels": true,
|
|
||||||
"asar": false,
|
|
||||||
"files": [
|
|
||||||
"!*.chunk.js.map",
|
|
||||||
"!*.chunk.js.LICENSE.txt",
|
|
||||||
"build/",
|
|
||||||
"!build/screenshots${/*}",
|
|
||||||
"!build/banner.jpg",
|
|
||||||
"!build/*.ico",
|
|
||||||
"!build/*.png",
|
|
||||||
"!node_modules${/*}",
|
|
||||||
"node_modules/better-sqlite3-multiple-ciphers/build/Release/better_sqlite3.node",
|
|
||||||
"node_modules/better-sqlite3-multiple-ciphers/lib",
|
|
||||||
"node_modules/better-sqlite3-multiple-ciphers/package.json",
|
|
||||||
"node_modules/file-uri-to-path",
|
|
||||||
"node_modules/bindings",
|
|
||||||
"node_modules/node-gyp-build",
|
|
||||||
"node_modules/sqlite-better-trigram",
|
|
||||||
"node_modules/sodium-native/prebuilds/${platform}-${arch}",
|
|
||||||
{
|
|
||||||
"from": "node_modules/sqlite-better-trigram-linux-${arch}",
|
|
||||||
"to": "node_modules/sqlite-better-trigram-linux-${arch}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "node_modules/sqlite-better-trigram-darwin-${arch}",
|
|
||||||
"to": "node_modules/sqlite-better-trigram-darwin-${arch}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "node_modules/sqlite-better-trigram-windows-${arch}",
|
|
||||||
"to": "node_modules/sqlite-better-trigram-windows-${arch}"
|
|
||||||
},
|
|
||||||
"node_modules/sodium-native/index.js",
|
|
||||||
"node_modules/sodium-native/package.json"
|
|
||||||
],
|
|
||||||
"afterPack": "./scripts/removeLocales.js",
|
|
||||||
"mac": {
|
|
||||||
"bundleVersion": "240",
|
|
||||||
"minimumSystemVersion": "10.12.0",
|
|
||||||
"target": [
|
|
||||||
{
|
|
||||||
"target": "dmg",
|
|
||||||
"arch": [
|
|
||||||
"arm64",
|
|
||||||
"x64"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"target": "zip",
|
|
||||||
"arch": [
|
|
||||||
"arm64",
|
|
||||||
"x64"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"category": "public.app-category.productivity",
|
|
||||||
"darkModeSupport": true,
|
|
||||||
"type": "distribution",
|
|
||||||
"hardenedRuntime": true,
|
|
||||||
"entitlements": "assets/entitlements.mac.plist",
|
|
||||||
"entitlementsInherit": "assets/entitlements.mac.plist",
|
|
||||||
"gatekeeperAssess": false,
|
|
||||||
"icon": "assets/icons/app.icns",
|
|
||||||
"notarize": true
|
|
||||||
},
|
|
||||||
"dmg": {
|
|
||||||
"contents": [
|
|
||||||
{
|
|
||||||
"x": 130,
|
|
||||||
"y": 220
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"x": 410,
|
|
||||||
"y": 220,
|
|
||||||
"type": "link",
|
|
||||||
"path": "/Applications"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"icon": "assets/icons/app.icns",
|
|
||||||
"title": "Install Notesnook"
|
|
||||||
},
|
|
||||||
"mas": {
|
|
||||||
"entitlements": "assets/entitlements.mas.plist",
|
|
||||||
"entitlementsInherit": "assets/entitlements.mas.inherit.plist",
|
|
||||||
"entitlementsLoginHelper": "assets/entitlements.mas.loginhelper.plist",
|
|
||||||
"hardenedRuntime": true
|
|
||||||
},
|
|
||||||
"win": {
|
|
||||||
"target": [
|
|
||||||
{
|
|
||||||
"target": "nsis",
|
|
||||||
"arch": [
|
|
||||||
"x64",
|
|
||||||
"arm64"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"target": "portable",
|
|
||||||
"arch": [
|
|
||||||
"x64",
|
|
||||||
"arm64"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"signtoolOptions": {
|
|
||||||
"signingHashAlgorithms": [
|
|
||||||
"sha256"
|
|
||||||
],
|
|
||||||
"sign": "./scripts/sign.js"
|
|
||||||
},
|
|
||||||
"icon": "assets/icons/app.ico"
|
|
||||||
},
|
|
||||||
"portable": {
|
|
||||||
"artifactName": "notesnook_${os}_${arch}_portable.${ext}"
|
|
||||||
},
|
|
||||||
"nsis": {
|
|
||||||
"oneClick": true,
|
|
||||||
"createDesktopShortcut": "always",
|
|
||||||
"deleteAppDataOnUninstall": true
|
|
||||||
},
|
|
||||||
"linux": {
|
|
||||||
"target": [
|
|
||||||
{
|
|
||||||
"target": "AppImage",
|
|
||||||
"arch": [
|
|
||||||
"x64",
|
|
||||||
"arm64"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"target": "snap",
|
|
||||||
"arch": [
|
|
||||||
"x64",
|
|
||||||
"arm64"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"category": "Office",
|
|
||||||
"icon": "assets/icons/app.icns",
|
|
||||||
"description": "Your private note taking space",
|
|
||||||
"executableName": "notesnook",
|
|
||||||
"desktop": {
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"id": "new-note",
|
|
||||||
"name": "New note",
|
|
||||||
"args": "new note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "new-notebook",
|
|
||||||
"name": "New notebook",
|
|
||||||
"args": "new notebook"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "new-reminder",
|
|
||||||
"name": "New reminder",
|
|
||||||
"args": "new reminder"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"snap": {
|
|
||||||
"autoStart": false,
|
|
||||||
"confinement": "strict",
|
|
||||||
"allowNativeWayland": true
|
|
||||||
},
|
|
||||||
"extraResources": [
|
|
||||||
"app-update.yml",
|
|
||||||
"./assets/**"
|
|
||||||
],
|
|
||||||
"extraMetadata": {
|
|
||||||
"main": "./build/electron.js"
|
|
||||||
},
|
|
||||||
"directories": {
|
|
||||||
"buildResources": "assets",
|
|
||||||
"output": "./output/"
|
|
||||||
},
|
|
||||||
"publish": [
|
|
||||||
{
|
|
||||||
"provider": "github",
|
|
||||||
"repo": "notesnook",
|
|
||||||
"owner": "streetwriters"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,11 @@ import { patchBetterSQLite3 } from "./patch-better-sqlite3.mjs";
|
|||||||
const args = yargs(process.argv);
|
const args = yargs(process.argv);
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
const root = args.root || path.join(__dirname, "..");
|
||||||
|
|
||||||
const webAppPath = path.resolve(path.join(__dirname, "..", "..", "web"));
|
const webAppPath = path.resolve(path.join(__dirname, "..", "..", "web"));
|
||||||
|
|
||||||
await fs.rm("./build/", { force: true, recursive: true });
|
await fs.rm(path.join(root, "build"), { force: true, recursive: true });
|
||||||
|
|
||||||
if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
|
if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
|
||||||
await exec(
|
await exec(
|
||||||
@@ -44,15 +45,15 @@ if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
|
|||||||
// temporary until there's support for prebuilt binaries for linux ARM
|
// temporary until there's support for prebuilt binaries for linux ARM
|
||||||
if (os.platform() === "linux") await patchBetterSQLite3();
|
if (os.platform() === "linux") await patchBetterSQLite3();
|
||||||
|
|
||||||
await fs.cp(path.join(webAppPath, "build"), "build", {
|
await fs.cp(path.join(webAppPath, "build"), path.join(root, "build"), {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
force: true
|
force: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (args.variant === "mas") {
|
if (args.variant === "mas") {
|
||||||
await exec(`yarn run bundle:mas`);
|
await exec(`yarn run bundle:mas --outdir=${path.join(root, "build")}`);
|
||||||
} else {
|
} else {
|
||||||
await exec(`yarn run bundle`);
|
await exec(`yarn run bundle --outdir=${path.join(root, "build")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await exec(`yarn run build`);
|
await exec(`yarn run build`);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { CancellationToken, autoUpdater } from "electron-updater";
|
|||||||
import type { AppUpdaterEvents } from "electron-updater/out/AppUpdater";
|
import type { AppUpdaterEvents } from "electron-updater/out/AppUpdater";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { config } from "../utils/config";
|
import { config } from "../utils/config";
|
||||||
|
import { app } from "electron";
|
||||||
|
|
||||||
type UpdateInfo = { version: string };
|
type UpdateInfo = { version: string };
|
||||||
type Progress = { percent: number };
|
type Progress = { percent: number };
|
||||||
@@ -33,6 +34,7 @@ let downloadTimeout: NodeJS.Timeout | undefined = undefined;
|
|||||||
|
|
||||||
export const updaterRouter = t.router({
|
export const updaterRouter = t.router({
|
||||||
autoUpdates: t.procedure.query(() => config.automaticUpdates),
|
autoUpdates: t.procedure.query(() => config.automaticUpdates),
|
||||||
|
releaseTrack: t.procedure.query(() => config.releaseTrack),
|
||||||
install: t.procedure.query(() => autoUpdater.quitAndInstall()),
|
install: t.procedure.query(() => autoUpdater.quitAndInstall()),
|
||||||
download: t.procedure.query(async () => {
|
download: t.procedure.query(async () => {
|
||||||
if (cancellationToken) return;
|
if (cancellationToken) return;
|
||||||
@@ -67,7 +69,13 @@ export const updaterRouter = t.router({
|
|||||||
.mutation(({ input: { enabled } }) => {
|
.mutation(({ input: { enabled } }) => {
|
||||||
config.automaticUpdates = enabled;
|
config.automaticUpdates = enabled;
|
||||||
}),
|
}),
|
||||||
|
changeReleaseTrack: t.procedure
|
||||||
|
.input(z.object({ track: z.string() }))
|
||||||
|
.mutation(({ input: { track } }) => {
|
||||||
|
config.releaseTrack = track;
|
||||||
|
app.relaunch();
|
||||||
|
app.exit();
|
||||||
|
}),
|
||||||
onChecking: createSubscription("checking-for-update"),
|
onChecking: createSubscription("checking-for-update"),
|
||||||
onDownloaded: createSubscription<"update-downloaded", UpdateInfo>(
|
onDownloaded: createSubscription<"update-downloaded", UpdateInfo>(
|
||||||
"update-downloaded"
|
"update-downloaded"
|
||||||
|
|||||||
@@ -20,19 +20,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
import { autoUpdater } from "electron-updater";
|
import { autoUpdater } from "electron-updater";
|
||||||
import { config } from "./config";
|
import { config } from "./config";
|
||||||
|
|
||||||
const CHANNEL = autoUpdater.currentVersion.raw.endsWith("-beta")
|
|
||||||
? "beta"
|
|
||||||
: "latest";
|
|
||||||
async function configureAutoUpdater() {
|
async function configureAutoUpdater() {
|
||||||
|
const releaseTrack =
|
||||||
|
config.releaseTrack === "stable" ? "latest" : config.releaseTrack;
|
||||||
autoUpdater.setFeedURL({
|
autoUpdater.setFeedURL({
|
||||||
provider: "generic",
|
provider: "generic",
|
||||||
url: `https://notesnook.com/api/v1/releases/${process.platform}/${CHANNEL}`,
|
url: `https://notesnook.com/api/v1/releases/${process.platform}/${releaseTrack}`,
|
||||||
useMultipleRangeRequest: false,
|
useMultipleRangeRequest: false,
|
||||||
channel: CHANNEL
|
channel: releaseTrack
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.autoDownload = config.automaticUpdates;
|
autoUpdater.autoDownload = config.automaticUpdates;
|
||||||
autoUpdater.allowDowngrade = false;
|
autoUpdater.allowDowngrade =
|
||||||
|
// only allow downgrade if the current version is a prerelease
|
||||||
|
// and the user has changed the release track to stable
|
||||||
|
config.releaseTrack === "stable" &&
|
||||||
|
autoUpdater.currentVersion.prerelease.length > 0;
|
||||||
autoUpdater.allowPrerelease = false;
|
autoUpdater.allowPrerelease = false;
|
||||||
autoUpdater.autoInstallOnAppQuit = true;
|
autoUpdater.autoInstallOnAppQuit = true;
|
||||||
autoUpdater.disableWebInstaller = true;
|
autoUpdater.disableWebInstaller = true;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
import { nativeTheme } from "electron";
|
import { nativeTheme } from "electron";
|
||||||
import { JSONStorage } from "./json-storage";
|
import { JSONStorage } from "./json-storage";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { autoUpdater } from "electron-updater";
|
||||||
|
|
||||||
export const DesktopIntegration = z.object({
|
export const DesktopIntegration = z.object({
|
||||||
autoStart: z.boolean().optional(),
|
autoStart: z.boolean().optional(),
|
||||||
@@ -46,6 +47,9 @@ export const config = {
|
|||||||
automaticUpdates: true,
|
automaticUpdates: true,
|
||||||
proxyRules: "",
|
proxyRules: "",
|
||||||
customDns: true,
|
customDns: true,
|
||||||
|
releaseTrack: autoUpdater.currentVersion.raw.includes("-beta")
|
||||||
|
? "beta"
|
||||||
|
: "stable",
|
||||||
|
|
||||||
backgroundColor: nativeTheme.themeSource === "dark" ? "#0f0f0f" : "#ffffff",
|
backgroundColor: nativeTheme.themeSource === "dark" ? "#0f0f0f" : "#ffffff",
|
||||||
windowControlsIconColor:
|
windowControlsIconColor:
|
||||||
|
|||||||
@@ -17,22 +17,24 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { _electron as electron } from "playwright";
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
const executablePath = process.env.EXECUTABLE_PATH;
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
// process.platform === "linux"
|
testTimeout: 30 * 1000,
|
||||||
// ? "output/linux-unpacked/Notesnook"
|
sequence: {
|
||||||
// : process.platform === "darwin"
|
concurrent: true,
|
||||||
// ? "output/mac/Notesnook.app/Contents/MacOS/Notesnook"
|
shuffle: true
|
||||||
// : "output/win-unpacked/Notesnook.exe";
|
},
|
||||||
|
dir: "./__tests__/",
|
||||||
export async function launchApp() {
|
exclude: [
|
||||||
const app = await electron.launch({
|
"**/node_modules/**",
|
||||||
executablePath
|
"**/dist/**",
|
||||||
});
|
"**/cypress/**",
|
||||||
|
"**/.{idea,git,cache,output,temp}/**",
|
||||||
const page = await app.firstWindow();
|
"**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*",
|
||||||
|
"**/test-results/**",
|
||||||
return { app, page };
|
"**/test-artifacts/**"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
});
|
||||||
@@ -70,6 +70,7 @@ function attachListeners() {
|
|||||||
function attachListener(event: string) {
|
function attachListener(event: string) {
|
||||||
return {
|
return {
|
||||||
onData(...args: any[]) {
|
onData(...args: any[]) {
|
||||||
|
console.log("Received data:", args);
|
||||||
AppEventManager.publish(event, ...args);
|
AppEventManager.publish(event, ...args);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ type DialogButtonProps = ButtonProps & {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
text: JSX.Element | string;
|
text: JSX.Element | string;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
role?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DialogProps = SxProp & {
|
type DialogProps = SxProp & {
|
||||||
@@ -190,6 +191,7 @@ function BaseDialog(props: React.PropsWithChildren<DialogProps>) {
|
|||||||
{...props.negativeButton}
|
{...props.negativeButton}
|
||||||
color="paragraph"
|
color="paragraph"
|
||||||
data-test-id="dialog-no"
|
data-test-id="dialog-no"
|
||||||
|
role="negative-button"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{props.positiveButton && (
|
{props.positiveButton && (
|
||||||
@@ -197,6 +199,7 @@ function BaseDialog(props: React.PropsWithChildren<DialogProps>) {
|
|||||||
{...props.positiveButton}
|
{...props.positiveButton}
|
||||||
color="accent"
|
color="accent"
|
||||||
data-test-id="dialog-yes"
|
data-test-id="dialog-yes"
|
||||||
|
role="positive-button"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -209,13 +212,21 @@ function BaseDialog(props: React.PropsWithChildren<DialogProps>) {
|
|||||||
|
|
||||||
export default BaseDialog;
|
export default BaseDialog;
|
||||||
|
|
||||||
export function DialogButton(props: DialogButtonProps) {
|
export function DialogButton({
|
||||||
|
disabled,
|
||||||
|
onClick,
|
||||||
|
loading,
|
||||||
|
text,
|
||||||
|
role,
|
||||||
|
...props
|
||||||
|
}: DialogButtonProps) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
{...props}
|
{...props}
|
||||||
variant="dialog"
|
variant="dialog"
|
||||||
disabled={props.disabled}
|
disabled={disabled}
|
||||||
onClick={props.disabled ? undefined : props.onClick}
|
onClick={disabled ? undefined : onClick}
|
||||||
|
data-role={role}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "100%",
|
maxWidth: "100%",
|
||||||
textOverflow: "ellipsis",
|
textOverflow: "ellipsis",
|
||||||
@@ -223,7 +234,7 @@ export function DialogButton(props: DialogButtonProps) {
|
|||||||
whiteSpace: "nowrap"
|
whiteSpace: "nowrap"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.loading ? <Loading size={16} color="accent" /> : props.text}
|
{loading ? <Loading size={16} color="accent" /> : text}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { clearLogs, downloadLogs } from "../../utils/logger";
|
|||||||
import { useAutoUpdateStore } from "../../hooks/use-auto-updater";
|
import { useAutoUpdateStore } from "../../hooks/use-auto-updater";
|
||||||
import { IssueDialog } from "../issue-dialog";
|
import { IssueDialog } from "../issue-dialog";
|
||||||
import { strings } from "@notesnook/intl";
|
import { strings } from "@notesnook/intl";
|
||||||
|
import { desktop } from "../../common/desktop-bridge";
|
||||||
|
|
||||||
export const AboutSettings: SettingsGroup[] = [
|
export const AboutSettings: SettingsGroup[] = [
|
||||||
{
|
{
|
||||||
@@ -90,7 +91,12 @@ export const AboutSettings: SettingsGroup[] = [
|
|||||||
value: "beta"
|
value: "beta"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
selectedOption: () => {
|
selectedOption: async () => {
|
||||||
|
if (IS_DESKTOP_APP)
|
||||||
|
return (
|
||||||
|
(await desktop?.updater.releaseTrack.query()) || "stable"
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
document.cookie
|
document.cookie
|
||||||
.split("; ")
|
.split("; ")
|
||||||
@@ -99,7 +105,11 @@ export const AboutSettings: SettingsGroup[] = [
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
async onSelectionChanged(value) {
|
async onSelectionChanged(value) {
|
||||||
document.cookie = `release-track=${value}; Secure`;
|
if (IS_DESKTOP_APP) {
|
||||||
|
return await desktop?.updater.changeReleaseTrack.mutate({
|
||||||
|
track: value
|
||||||
|
});
|
||||||
|
}
|
||||||
const registrations =
|
const registrations =
|
||||||
(await navigator.serviceWorker?.getRegistrations()) || [];
|
(await navigator.serviceWorker?.getRegistrations()) || [];
|
||||||
for (const registration of registrations) {
|
for (const registration of registrations) {
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ self.addEventListener("message", (event) => {
|
|||||||
event.source.postMessage({
|
event.source.postMessage({
|
||||||
type: data.type,
|
type: data.type,
|
||||||
version: APP_VERSION,
|
version: APP_VERSION,
|
||||||
hash: GIT_HASH
|
hash: GIT_HASH,
|
||||||
|
isBeta: IS_BETA
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -20,26 +20,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
export type Platforms = "web" | "desktop";
|
export type Platforms = "web" | "desktop";
|
||||||
export type AppVersion = typeof appVersion;
|
export type AppVersion = typeof appVersion;
|
||||||
export const appVersion = {
|
export const appVersion = {
|
||||||
formatted: format(APP_VERSION, GIT_HASH, PLATFORM, IS_BETA),
|
formatted: format(APP_VERSION, GIT_HASH, PLATFORM),
|
||||||
clean: formatVersion(APP_VERSION),
|
clean: APP_VERSION,
|
||||||
numerical: parseInt(APP_VERSION || "0"),
|
numerical: versionAsNumber(APP_VERSION),
|
||||||
hash: GIT_HASH,
|
hash: GIT_HASH,
|
||||||
isBeta: IS_BETA
|
isBeta: IS_BETA
|
||||||
};
|
};
|
||||||
|
|
||||||
function format(
|
function format(version?: string, hash?: string, type?: "web" | "desktop") {
|
||||||
version?: string,
|
return `${version}-${hash}-${type}`;
|
||||||
hash?: string,
|
|
||||||
type?: "web" | "desktop",
|
|
||||||
beta?: boolean
|
|
||||||
) {
|
|
||||||
return `${formatVersion(version)}-${hash}-${type}${beta ? "-beta" : ""}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatVersion(version?: string) {
|
function versionAsNumber(version: string) {
|
||||||
if (!version) return "";
|
return parseInt(version.replace(/\D/g, ""));
|
||||||
const [major, minor, bugfix0, bugfix1] = version.toString().split("");
|
|
||||||
return `${major}.${minor}.${bugfix0}${bugfix1 || ""}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getServiceWorkerVersion(
|
export function getServiceWorkerVersion(
|
||||||
@@ -55,13 +48,13 @@ export function getServiceWorkerVersion(
|
|||||||
if (type !== "GET_VERSION") return;
|
if (type !== "GET_VERSION") return;
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
|
|
||||||
const { version, hash } = ev.data;
|
const { version, hash, isBeta } = ev.data;
|
||||||
resolve({
|
resolve({
|
||||||
formatted: formatVersion(version),
|
formatted: format(version, hash, PLATFORM),
|
||||||
numerical: parseInt(version),
|
numerical: versionAsNumber(version),
|
||||||
clean: formatVersion(version),
|
clean: version,
|
||||||
hash,
|
hash,
|
||||||
isBeta: appVersion.isBeta
|
isBeta
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
serviceWorker.postMessage({ type: "GET_VERSION" });
|
serviceWorker.postMessage({ type: "GET_VERSION" });
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ const gitHash = (() => {
|
|||||||
return process.env.GIT_HASH || "gitless";
|
return process.env.GIT_HASH || "gitless";
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
const appVersion = version.replaceAll(".", "").replace("-beta", "");
|
// const appVersion = version.replaceAll(".", "").replace("-beta", "");
|
||||||
const isBeta = process.env.BETA === "true";
|
const isBeta = version.includes("-beta");
|
||||||
const isTesting =
|
const isTesting =
|
||||||
process.env.TEST === "true" || process.env.NODE_ENV === "development";
|
process.env.TEST === "true" || process.env.NODE_ENV === "development";
|
||||||
const isDesktop = process.env.PLATFORM === "desktop";
|
const isDesktop = process.env.PLATFORM === "desktop";
|
||||||
@@ -76,7 +76,7 @@ export default defineConfig({
|
|||||||
define: {
|
define: {
|
||||||
APP_TITLE: `"${isThemeBuilder ? "Notesnook Theme Builder" : "Notesnook"}"`,
|
APP_TITLE: `"${isThemeBuilder ? "Notesnook Theme Builder" : "Notesnook"}"`,
|
||||||
GIT_HASH: `"${gitHash}"`,
|
GIT_HASH: `"${gitHash}"`,
|
||||||
APP_VERSION: `"${appVersion}"`,
|
APP_VERSION: `"${version}"`,
|
||||||
PUBLIC_URL: `"${process.env.PUBLIC_URL || ""}"`,
|
PUBLIC_URL: `"${process.env.PUBLIC_URL || ""}"`,
|
||||||
IS_DESKTOP_APP: isDesktop,
|
IS_DESKTOP_APP: isDesktop,
|
||||||
PLATFORM: `"${process.env.PLATFORM}"`,
|
PLATFORM: `"${process.env.PLATFORM}"`,
|
||||||
|
|||||||
Reference in New Issue
Block a user