mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
feat: implement desktop logic in a seperate folder
This commit is contained in:
2
apps/web/desktop/.gitignore
vendored
Normal file
2
apps/web/desktop/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
build
|
||||
10
apps/web/desktop/assets/entitlements.mac.plist
Normal file
10
apps/web/desktop/assets/entitlements.mac.plist
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
apps/web/desktop/assets/icons/app.icns
Normal file
BIN
apps/web/desktop/assets/icons/app.icns
Normal file
Binary file not shown.
BIN
apps/web/desktop/assets/icons/app.ico
Normal file
BIN
apps/web/desktop/assets/icons/app.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 353 KiB |
29
apps/web/desktop/autoupdate.js
Normal file
29
apps/web/desktop/autoupdate.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const { autoUpdater } = require("electron-updater");
|
||||
const { EVENTS } = require("./events");
|
||||
const { sendMessageToRenderer } = require("./ipc");
|
||||
|
||||
async function configureAutoUpdater() {
|
||||
autoUpdater.autoDownload = false;
|
||||
autoUpdater.allowDowngrade = false;
|
||||
autoUpdater.allowPrerelease = false;
|
||||
autoUpdater.autoInstallOnAppQuit = true;
|
||||
autoUpdater.addListener("checking-for-update", () => {
|
||||
sendMessageToRenderer(EVENTS.checkingForUpdate);
|
||||
});
|
||||
autoUpdater.addListener("update-available", (info) => {
|
||||
sendMessageToRenderer(EVENTS.updateAvailable, info);
|
||||
});
|
||||
autoUpdater.addListener("download-progress", (progress) => {
|
||||
console.log("Downloading", progress);
|
||||
sendMessageToRenderer(EVENTS.updateDownloadProgress, progress);
|
||||
});
|
||||
autoUpdater.addListener("update-downloaded", (info) => {
|
||||
sendMessageToRenderer(EVENTS.updateDownloadCompleted, info);
|
||||
});
|
||||
autoUpdater.addListener("update-not-available", (info) => {
|
||||
sendMessageToRenderer(EVENTS.updateNotAvailable, info);
|
||||
});
|
||||
await autoUpdater.checkForUpdates();
|
||||
}
|
||||
|
||||
module.exports = { configureAutoUpdater };
|
||||
3
apps/web/desktop/dev-app-update.yml
Normal file
3
apps/web/desktop/dev-app-update.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
owner: streetwriters
|
||||
repo: notesnook
|
||||
provider: github
|
||||
68
apps/web/desktop/electron.js
Normal file
68
apps/web/desktop/electron.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const { app, BrowserWindow } = require("electron");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
const { isDevelopment } = require("./utils");
|
||||
const { registerProtocol, URL } = require("./protocol");
|
||||
const { configureAutoUpdater } = require("./autoupdate");
|
||||
require("./ipc/index.js");
|
||||
|
||||
try {
|
||||
require("electron-reloader")(module);
|
||||
} catch (_) {}
|
||||
|
||||
let mainWindow;
|
||||
|
||||
async function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
autoHideMenuBar: true,
|
||||
icon: path.join(
|
||||
__dirname,
|
||||
os.platform() === "win32" ? "app.ico" : "favicon-72x72.png"
|
||||
),
|
||||
webPreferences: {
|
||||
devTools: true, // isDev,
|
||||
nodeIntegration: false, //true,
|
||||
enableRemoteModule: false,
|
||||
contextIsolation: true,
|
||||
sandbox: true,
|
||||
preload: __dirname + "/preload.js",
|
||||
},
|
||||
});
|
||||
|
||||
mainWindow.on("show", () => {
|
||||
console.log("SHOWIGN!");
|
||||
});
|
||||
|
||||
if (isDevelopment())
|
||||
mainWindow.webContents.openDevTools({ mode: "right", activate: true });
|
||||
|
||||
mainWindow.maximize();
|
||||
|
||||
// await loadURL(mainWindow);
|
||||
mainWindow.loadURL(isDevelopment() ? process.env.ELECTRON_START_URL : URL);
|
||||
|
||||
mainWindow.on("closed", () => {
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
global.win = mainWindow;
|
||||
}
|
||||
|
||||
app.commandLine.appendSwitch("lang", "en-US");
|
||||
app.on("ready", async () => {
|
||||
registerProtocol();
|
||||
await createWindow();
|
||||
configureAutoUpdater();
|
||||
});
|
||||
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
7
apps/web/desktop/events.js
Normal file
7
apps/web/desktop/events.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports.EVENTS = {
|
||||
checkingForUpdate: "checkingForUpdate",
|
||||
updateAvailable: "updateAvailable",
|
||||
updateDownloadProgress: "updateDownloadProgress",
|
||||
updateDownloadCompleted: "updateDownloadCompleted",
|
||||
updateNotAvailable: "updateNotAvailable",
|
||||
};
|
||||
13
apps/web/desktop/ipc/actions/downloadUpdate.js
Normal file
13
apps/web/desktop/ipc/actions/downloadUpdate.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const { CancellationToken } = require("builder-util-runtime");
|
||||
const { autoUpdater } = require("electron-updater");
|
||||
const { sendMessageToRenderer } = require("..");
|
||||
const { EVENTS } = require("../../events");
|
||||
|
||||
module.exports = {
|
||||
type: "downloadUpdate",
|
||||
action: () => {
|
||||
sendMessageToRenderer(EVENTS.updateDownloadProgress, { progress: 0 });
|
||||
autoUpdater.cancellationToken = new CancellationToken();
|
||||
autoUpdater.downloadUpdate(autoUpdater.cancellationToken);
|
||||
},
|
||||
};
|
||||
10
apps/web/desktop/ipc/actions/index.js
Normal file
10
apps/web/desktop/ipc/actions/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const actions = {};
|
||||
|
||||
module.exports.getAction = function getAction(actionName) {
|
||||
try {
|
||||
if (!actions[actionName]) actions[actionName] = require(`./${actionName}`);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return actions[actionName];
|
||||
};
|
||||
8
apps/web/desktop/ipc/actions/installUpdate.js
Normal file
8
apps/web/desktop/ipc/actions/installUpdate.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const { autoUpdater } = require("electron-updater");
|
||||
|
||||
module.exports = {
|
||||
type: "installUpdate",
|
||||
action: () => {
|
||||
autoUpdater.quitAndInstall();
|
||||
},
|
||||
};
|
||||
8
apps/web/desktop/ipc/actions/openLink.js
Normal file
8
apps/web/desktop/ipc/actions/openLink.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const { shell } = require("electron");
|
||||
module.exports = {
|
||||
type: "openLink",
|
||||
action: (args) => {
|
||||
const { link } = args;
|
||||
return shell.openExternal(link);
|
||||
},
|
||||
};
|
||||
23
apps/web/desktop/ipc/actions/saveFile.js
Normal file
23
apps/web/desktop/ipc/actions/saveFile.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const { app } = require("electron");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
type: "saveFile",
|
||||
action: (args) => {
|
||||
const { data, filePath } = args;
|
||||
if (!data || !filePath) return;
|
||||
const resolvedPath = path.join(
|
||||
...filePath.split("/").map((segment) => {
|
||||
let resolved = segment;
|
||||
try {
|
||||
resolved = app.getPath(resolved);
|
||||
} finally {
|
||||
return resolved;
|
||||
}
|
||||
})
|
||||
);
|
||||
fs.mkdirSync(path.dirname(resolvedPath), { recursive: true });
|
||||
fs.writeFileSync(resolvedPath, data);
|
||||
},
|
||||
};
|
||||
16
apps/web/desktop/ipc/index.js
Normal file
16
apps/web/desktop/ipc/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const { ipcMain, BrowserWindow } = require("electron");
|
||||
const { getAction } = require("./actions");
|
||||
|
||||
ipcMain.on("fromRenderer", async (event, args) => {
|
||||
const { type } = args;
|
||||
const action = getAction(type);
|
||||
if (!action) return;
|
||||
await action.action(args);
|
||||
});
|
||||
|
||||
module.exports.sendMessageToRenderer = function (type, payload = {}) {
|
||||
global.win.webContents.send("fromMain", {
|
||||
type,
|
||||
...payload,
|
||||
});
|
||||
};
|
||||
130
apps/web/desktop/package.json
Normal file
130
apps/web/desktop/package.json
Normal file
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "1.4.1",
|
||||
"private": true,
|
||||
"main": "./electron.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
"repository": "https://github.com/streetwriters/notesnook",
|
||||
"dependencies": {
|
||||
"electron-serve": "^1.1.0",
|
||||
"electron-updater": "^4.3.8",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node-fetch": "^2.5.10",
|
||||
"copyfiles": "^2.4.1",
|
||||
"electron": "^12.0.0",
|
||||
"electron-builder": "^22.11.7",
|
||||
"electron-builder-notarize": "^1.2.0",
|
||||
"electron-reloader": "^1.2.1",
|
||||
"esbuild": "^0.12.15"
|
||||
},
|
||||
"scripts": {
|
||||
"bundle": "esbuild ./electron.js ./preload.js --minify --external:electron --bundle --outdir=./build --platform=node",
|
||||
"release": "./scripts/publish.sh"
|
||||
},
|
||||
"author": {
|
||||
"name": "Streetwriters (Private) Ltd.",
|
||||
"email": "support@streetwriters.co",
|
||||
"url": "https://streetwriters.co"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.streetwriters.notesnook",
|
||||
"productName": "Notesnook",
|
||||
"copyright": "Copyright © 2021 Streetwriters (Private) Ltd.",
|
||||
"artifactName": "notesnook.${ext}",
|
||||
"files": [
|
||||
"!*.chunk.js.map",
|
||||
"!*.chunk.js.LICENSE.txt",
|
||||
"build/",
|
||||
"!node_modules",
|
||||
"node_modules/node-fetch",
|
||||
"node_modules/electron-updater",
|
||||
"node_modules/lazy-val",
|
||||
"node_modules/builder-util-runtime",
|
||||
"node_modules/fs-extra",
|
||||
"node_modules/js-yaml",
|
||||
"node_modules/lodash.isequal",
|
||||
"node_modules/semver",
|
||||
"node_modules/debug",
|
||||
"node_modules/ms",
|
||||
"node_modules/lru-cache",
|
||||
"node_modules/yallist",
|
||||
"node_modules/universalify",
|
||||
"node_modules/graceful-fs",
|
||||
"node_modules/at-least-node",
|
||||
"node_modules/jsonfile",
|
||||
"node_modules/sax"
|
||||
],
|
||||
"afterSign": "electron-builder-notarize",
|
||||
"afterPack": "./scripts/removeLocales.js",
|
||||
"mac": {
|
||||
"target": [
|
||||
"dmg",
|
||||
"zip"
|
||||
],
|
||||
"electronLanguages": [
|
||||
"en-US"
|
||||
],
|
||||
"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"
|
||||
},
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{
|
||||
"x": 130,
|
||||
"y": 220
|
||||
},
|
||||
{
|
||||
"x": 410,
|
||||
"y": 220,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
}
|
||||
],
|
||||
"icon": "assets/icons/app.icns",
|
||||
"title": "Install Notesnook"
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
"nsis"
|
||||
],
|
||||
"icon": "assets/icons/app.ico"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": true,
|
||||
"createDesktopShortcut": "always"
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
"AppImage",
|
||||
"deb",
|
||||
"rpm"
|
||||
],
|
||||
"category": "Office",
|
||||
"icon": "assets/icons/app.icns",
|
||||
"description": "Your private note taking space",
|
||||
"executableName": "Notesnook"
|
||||
},
|
||||
"extraResources": [
|
||||
"./assets/**"
|
||||
],
|
||||
"directories": {
|
||||
"buildResources": "assets",
|
||||
"output": "./dist/"
|
||||
},
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"repo": "notesnook",
|
||||
"owner": "streetwriters"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
apps/web/desktop/paths.js
Normal file
3
apps/web/desktop/paths.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports.PATHS = {
|
||||
backupsDirectory: "documents/Notesnook/backups",
|
||||
};
|
||||
22
apps/web/desktop/preload.js
Normal file
22
apps/web/desktop/preload.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
|
||||
// Expose protected methods that allow the renderer process to use
|
||||
// the ipcRenderer without exposing the entire object
|
||||
contextBridge.exposeInMainWorld("api", {
|
||||
send: (channel, data) => {
|
||||
// whitelist channels
|
||||
let validChannels = ["fromRenderer"];
|
||||
if (validChannels.includes(channel)) {
|
||||
ipcRenderer.send(channel, data);
|
||||
}
|
||||
},
|
||||
receive: (channel, func) => {
|
||||
let validChannels = ["fromMain"];
|
||||
if (validChannels.includes(channel)) {
|
||||
// Deliberately strip event as it includes `sender`
|
||||
ipcRenderer.on(channel, (event, args) => {
|
||||
func(args);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
65
apps/web/desktop/protocol.js
Normal file
65
apps/web/desktop/protocol.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const { protocol } = require("electron");
|
||||
const { isDevelopment, getPath } = require("./utils");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const fetch = require("node-fetch").default;
|
||||
|
||||
const FILE_NOT_FOUND = -6;
|
||||
const BASE_PATH = isDevelopment() ? "../public" : "";
|
||||
const HOSTNAME = `app.notesnook.com`;
|
||||
const PROTOCOL = "https";
|
||||
const extensionToMimeType = {
|
||||
html: "text/html",
|
||||
json: "application/json",
|
||||
js: "application/javascript",
|
||||
css: "text/css",
|
||||
svg: "image/svg+xml",
|
||||
png: "image/png",
|
||||
};
|
||||
|
||||
function registerProtocol() {
|
||||
protocol.interceptStreamProtocol(
|
||||
PROTOCOL,
|
||||
async (request, callback) => {
|
||||
const url = new URL(request.url);
|
||||
if (url.hostname === HOSTNAME) {
|
||||
const absoluteFilePath = path.normalize(
|
||||
`${__dirname}${
|
||||
url.pathname === "/"
|
||||
? `${BASE_PATH}/index.html`
|
||||
: `${BASE_PATH}/${url.pathname}`
|
||||
}`
|
||||
);
|
||||
const filePath = getPath(absoluteFilePath);
|
||||
if (!filePath) {
|
||||
callback({ error: FILE_NOT_FOUND });
|
||||
return;
|
||||
}
|
||||
const fileExtension = path.extname(filePath).replace(".", "");
|
||||
|
||||
const data = fs.createReadStream(filePath);
|
||||
callback({
|
||||
data,
|
||||
mimeType: extensionToMimeType[fileExtension],
|
||||
});
|
||||
} else {
|
||||
const response = await fetch(request.url, {
|
||||
...request,
|
||||
body: !!request.uploadData ? request.uploadData[0].bytes : null,
|
||||
headers: { ...request.headers, origin: `${PROTOCOL}://${HOSTNAME}/` },
|
||||
});
|
||||
callback({
|
||||
data: response.body,
|
||||
headers: response.headers,
|
||||
mimeType: response.headers.get("Content-Type"),
|
||||
statusCode: response.status,
|
||||
});
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
if (err) console.error("Failed to register protocol");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { registerProtocol, URL: `${PROTOCOL}://${HOSTNAME}/` };
|
||||
30
apps/web/desktop/scripts/publish.sh
Executable file
30
apps/web/desktop/scripts/publish.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
# Go to notes-web directory
|
||||
cd ../
|
||||
|
||||
# Generate the web app build
|
||||
if ! yarn build:desktop; then
|
||||
echo "Failed to build web app."
|
||||
exit
|
||||
fi
|
||||
|
||||
# Change back to desktop directory
|
||||
cd desktop
|
||||
|
||||
# Copy build files to desktop folder
|
||||
# NOTE: we have to do this because electron-builder cannot work in parent directories.
|
||||
if ! yarn copyfiles -a ../build ./build; then
|
||||
echo "Could not copy build files."
|
||||
exit
|
||||
fi
|
||||
|
||||
# Generate electron specific bundle
|
||||
if ! yarn bundle; then
|
||||
echo "Could not generate electron bundle"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Build and publish
|
||||
if ! electron-builder -c.extraMetadata.main=./build/electron.js; then
|
||||
echo "Failed to build and publish electron builds"
|
||||
exit
|
||||
fi
|
||||
17
apps/web/desktop/scripts/removeLocales.js
Normal file
17
apps/web/desktop/scripts/removeLocales.js
Normal file
@@ -0,0 +1,17 @@
|
||||
//https://www.electron.build/configuration/configuration#afterpack
|
||||
exports.default = async function (context) {
|
||||
//console.log(context)
|
||||
var fs = require("fs");
|
||||
var localeDir = context.appOutDir + "/locales/";
|
||||
|
||||
fs.readdir(localeDir, function (err, files) {
|
||||
//files is array of filenames (basename form)
|
||||
if (!(files && files.length)) return;
|
||||
for (var i = 0, len = files.length; i < len; i++) {
|
||||
var match = files[i].match(/en-US\.pak/);
|
||||
if (match === null) {
|
||||
fs.unlinkSync(localeDir + files[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
29
apps/web/desktop/utils.js
Normal file
29
apps/web/desktop/utils.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const { app } = require("electron");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
function isDevelopment() {
|
||||
if (typeof electron === "string") {
|
||||
throw new TypeError("Not running in an Electron environment!");
|
||||
}
|
||||
|
||||
const isEnvSet = "ELECTRON_IS_DEV" in process.env;
|
||||
const getFromEnv = Number.parseInt(process.env.ELECTRON_IS_DEV, 10) === 1;
|
||||
return isEnvSet ? getFromEnv : !app.isPackaged;
|
||||
}
|
||||
|
||||
function getPath(filePath) {
|
||||
try {
|
||||
const result = fs.statSync(filePath);
|
||||
|
||||
if (result.isFile()) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
if (result.isDirectory()) {
|
||||
return getPath(path.join(filePath, "index.html"));
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
module.exports = { getPath, isDevelopment };
|
||||
2420
apps/web/desktop/yarn.lock
Normal file
2420
apps/web/desktop/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user