From f3dbc7be525418efedcbf004c94c1e482322a9ae Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 19 Jan 2025 17:11:58 -0800 Subject: [PATCH 01/20] Register and handle cyd:// URLs --- src/main.ts | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/main.ts b/src/main.ts index 73a5489b..764dde83 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,7 @@ import { webContents, nativeImage, autoUpdater, + protocol, powerSaveBlocker, powerMonitor, FileFilter @@ -71,6 +72,16 @@ if (!app.requestSingleInstanceLock()) { process.exit(0); } +// Register the cyd:// protocol scheme +protocol.registerSchemesAsPrivileged([ + { + scheme: "cyd", + privileges: { + standard: true, + }, + }, +]); + // Initialize the logger log.initialize(); log.transports.file.level = false; // Disable file logging @@ -169,6 +180,49 @@ async function createWindow() { icon: icon, }); + // Protocol handler for cyd:// URLs + protocol.handle('cyd', (req) => { + log.info('Received cyd:// URL:', req.url); + const { hostname, pathname } = new URL(req.url) + + // Validate the hostname + const validHostnames = ['social.cyd.api', 'social.cyd.dev-api']; + if (!validHostnames.includes(hostname)) { + dialog.showMessageBoxSync({ + title: "Cyd", + message: `Invalid hostname in cyd:// URL: ${req.url}`, + type: 'info', + }); + return new Response('bad', { + status: 400, + headers: { 'content-type': 'text/html' } + }) + } + + // Make sure the pathname is supported + if (pathname == "open") { + // Supported! Do nothing, since the app should now be opened + log.info('protocol handler opening app'); + } else { + dialog.showMessageBoxSync({ + title: "Cyd", + message: `Invalid path in cyd:// URL: ${req.url}`, + type: 'info', + }); + return new Response('bad', { + status: 400, + headers: { 'content-type': 'text/html' } + }) + } + + // Return a successful response + log.info('protocol handler returning ok'); + return new Response('ok', { + status: 200, + headers: { 'content-type': 'text/html' } + }) + }) + // Handle power monitor events powerMonitor.on('suspend', () => { From ac59b8850edc3d2535046dc270430bdd3a18cd75 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 11:41:28 -0800 Subject: [PATCH 02/20] Switch from protocol to deep links --- src/main.ts | 86 ++++++++++++++++++++--------------------------------- 1 file changed, 32 insertions(+), 54 deletions(-) diff --git a/src/main.ts b/src/main.ts index 764dde83..27f89909 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,7 +13,6 @@ import { webContents, nativeImage, autoUpdater, - protocol, powerSaveBlocker, powerMonitor, FileFilter @@ -72,16 +71,6 @@ if (!app.requestSingleInstanceLock()) { process.exit(0); } -// Register the cyd:// protocol scheme -protocol.registerSchemesAsPrivileged([ - { - scheme: "cyd", - privileges: { - standard: true, - }, - }, -]); - // Initialize the logger log.initialize(); log.transports.file.level = false; // Disable file logging @@ -180,49 +169,6 @@ async function createWindow() { icon: icon, }); - // Protocol handler for cyd:// URLs - protocol.handle('cyd', (req) => { - log.info('Received cyd:// URL:', req.url); - const { hostname, pathname } = new URL(req.url) - - // Validate the hostname - const validHostnames = ['social.cyd.api', 'social.cyd.dev-api']; - if (!validHostnames.includes(hostname)) { - dialog.showMessageBoxSync({ - title: "Cyd", - message: `Invalid hostname in cyd:// URL: ${req.url}`, - type: 'info', - }); - return new Response('bad', { - status: 400, - headers: { 'content-type': 'text/html' } - }) - } - - // Make sure the pathname is supported - if (pathname == "open") { - // Supported! Do nothing, since the app should now be opened - log.info('protocol handler opening app'); - } else { - dialog.showMessageBoxSync({ - title: "Cyd", - message: `Invalid path in cyd:// URL: ${req.url}`, - type: 'info', - }); - return new Response('bad', { - status: 400, - headers: { 'content-type': 'text/html' } - }) - } - - // Return a successful response - log.info('protocol handler returning ok'); - return new Response('ok', { - status: 200, - headers: { 'content-type': 'text/html' } - }) - }) - // Handle power monitor events powerMonitor.on('suspend', () => { @@ -552,3 +498,35 @@ app.on('activate', () => { createWindow(); } }); + +// Handle cyd:// URLs (or cyd-dev:// in dev mode) +app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev"); +app.on('open-url', (event, cydURL) => { + const url = new URL(cydURL); + log.info(`Received ${url.protocol}:// URL: ${url.toString()}`); + + // Validate the hostname + if ((config.mode == "prod" && url.hostname != 'social.cyd.api') || (config.mode == "dev" && url.hostname != 'social.cyd.dev-api')) { + dialog.showMessageBoxSync({ + title: "Cyd", + message: `Invalid hostname in cyd:// URL: ${url}`, + type: 'info', + }); + return; + } + + // Handle /open + if (url.pathname == "/open") { + // Supported! Do nothing, since the app should now be opened + log.info('protocol handler opening app'); + } + // For all other paths, show an error + else { + dialog.showMessageBoxSync({ + title: "Cyd", + message: `Invalid path in ${url.protocol}:// URL: ${url.toString()}`, + type: 'info', + }); + return; + } +}) From 8b21e22eda4f73cc9686595a04087c5a2538ef8d Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 12:01:35 -0800 Subject: [PATCH 03/20] Handle deep links for both Mac and Windows/Linux --- src/main.ts | 120 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 79 insertions(+), 41 deletions(-) diff --git a/src/main.ts b/src/main.ts index 27f89909..3a18f7b5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -66,11 +66,51 @@ if (require('electron-squirrel-startup')) { app.quit(); } -if (!app.requestSingleInstanceLock()) { - app.quit(); - process.exit(0); + +// Handle cyd:// URLs (or cyd-dev:// in dev mode) +// See: https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app +if (process.defaultApp) { + if (process.argv.length >= 2) { + app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev", process.execPath, [path.resolve(process.argv[1])]) + } +} else { + app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev") +} + +const openCydURL = (cydURL: string) => { + const url = new URL(cydURL); + log.info(`Received ${url.protocol}:// URL: ${url.toString()}`); + + // Validate the hostname + if ((config.mode == "prod" && url.hostname != 'social.cyd.api') || (config.mode == "dev" && url.hostname != 'social.cyd.dev-api')) { + dialog.showMessageBoxSync({ + title: "Cyd", + message: `Invalid hostname in cyd:// URL: ${url}`, + type: 'info', + }); + return; + } + + // Handle /open + if (url.pathname == "/open") { + // Supported! Do nothing, since the app should now be opened + log.info('protocol handler opening app'); + } + // For all other paths, show an error + else { + dialog.showMessageBoxSync({ + title: "Cyd", + message: `Invalid path in ${url.protocol}:// URL: ${url.toString()}`, + type: 'info', + }); + return; + } } +app.on('open-url', (event, url) => { + openCydURL(url); +}) + // Initialize the logger log.initialize(); log.transports.file.level = false; // Disable file logging @@ -154,10 +194,11 @@ async function initializeApp() { await createWindow(); } +let win: BrowserWindow | null = null; async function createWindow() { // Create the browser window const icon = nativeImage.createFromPath(path.join(getResourcesPath(), 'icon.png')); - const win = new BrowserWindow({ + win = new BrowserWindow({ width: 1000, height: 850, minWidth: 900, @@ -174,7 +215,7 @@ async function createWindow() { powerMonitor.on('suspend', () => { log.info('System is suspending'); try { - win.webContents.send('powerMonitor:suspend'); + win?.webContents.send('powerMonitor:suspend'); } catch (error) { log.error('Failed to send powerMonitor:suspend to renderer:', error); } @@ -183,7 +224,7 @@ async function createWindow() { powerMonitor.on('resume', () => { log.info('System has resumed'); try { - win.webContents.send('powerMonitor:resume'); + win?.webContents.send('powerMonitor:resume'); } catch (error) { log.error('Failed to send powerMonitor:resume to renderer:', error); } @@ -366,6 +407,9 @@ async function createWindow() { } try { + if (!win) { + throw new Error("Window not initialized"); + } const result = dialog.showOpenDialogSync(win, options); if (result && result.length > 0) { return result[0]; @@ -473,15 +517,41 @@ async function createWindow() { // When devtools opens, make sure the window is wide enough win.webContents.on('devtools-opened', () => { - const [width, height] = win.getSize(); - if (width < 1500) { - win.setSize(1500, height); + if (win) { + const [width, height] = win.getSize(); + if (width < 1500) { + win.setSize(1500, height); + } } }); return win; } +// Make sure there's only one instance of the app running +if (!app.requestSingleInstanceLock()) { + app.quit(); + process.exit(0); +} else { + app.on('second-instance', (event, commandLine, _) => { + // Someone tried to run a second instance, focus the window + if (win) { + if (win.isMinimized()) win.restore() + win.focus() + } + // commandLine is array of strings in which last element is deep link URL + const cydURL = commandLine.pop() + if (cydURL) { + openCydURL(cydURL); + } + }) + + // Initialize the app + app.whenReady().then(async () => { + await initializeApp(); + }) +} + app.enableSandbox(); app.on('ready', initializeApp); @@ -498,35 +568,3 @@ app.on('activate', () => { createWindow(); } }); - -// Handle cyd:// URLs (or cyd-dev:// in dev mode) -app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev"); -app.on('open-url', (event, cydURL) => { - const url = new URL(cydURL); - log.info(`Received ${url.protocol}:// URL: ${url.toString()}`); - - // Validate the hostname - if ((config.mode == "prod" && url.hostname != 'social.cyd.api') || (config.mode == "dev" && url.hostname != 'social.cyd.dev-api')) { - dialog.showMessageBoxSync({ - title: "Cyd", - message: `Invalid hostname in cyd:// URL: ${url}`, - type: 'info', - }); - return; - } - - // Handle /open - if (url.pathname == "/open") { - // Supported! Do nothing, since the app should now be opened - log.info('protocol handler opening app'); - } - // For all other paths, show an error - else { - dialog.showMessageBoxSync({ - title: "Cyd", - message: `Invalid path in ${url.protocol}:// URL: ${url.toString()}`, - type: 'info', - }); - return; - } -}) From ab385f110c68b76fd8015c83dd66c1a50c0f4f48 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 12:35:06 -0800 Subject: [PATCH 04/20] Queue cyd:// links and process them after that app is ready --- src/main.ts | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/main.ts b/src/main.ts index 3a18f7b5..dc70ed3c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -42,6 +42,11 @@ interface Config { plausibleDomain: string; } +let isAppReady = false; + +// Queue of cyd:// URLs to handle, in case the app is not ready +const cydURLQueue: string[] = []; + // Load the config const configPath = path.join(getResourcesPath(), 'config.json'); if (!fs.existsSync(configPath)) { @@ -77,15 +82,21 @@ if (process.defaultApp) { app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev") } -const openCydURL = (cydURL: string) => { +const openCydURL = async (cydURL: string) => { const url = new URL(cydURL); log.info(`Received ${url.protocol}:// URL: ${url.toString()}`); + // If there's no main window, open one + if (BrowserWindow.getAllWindows().length === 0) { + await createWindow(); + } + // Validate the hostname - if ((config.mode == "prod" && url.hostname != 'social.cyd.api') || (config.mode == "dev" && url.hostname != 'social.cyd.dev-api')) { + const validHostname = config.mode == "prod" ? 'social.cyd.api' : 'social.cyd.dev-api'; + if (url.hostname != validHostname) { dialog.showMessageBoxSync({ title: "Cyd", - message: `Invalid hostname in cyd:// URL: ${url}`, + message: `Invalid hostname in URL ${url.toString()}. I'm expecting "${validHostname}" as the hostname.`, type: 'info', }); return; @@ -100,7 +111,7 @@ const openCydURL = (cydURL: string) => { else { dialog.showMessageBoxSync({ title: "Cyd", - message: `Invalid path in ${url.protocol}:// URL: ${url.toString()}`, + message: `Invalid Cyd URL: ${url.toString()}.`, type: 'info', }); return; @@ -108,7 +119,11 @@ const openCydURL = (cydURL: string) => { } app.on('open-url', (event, url) => { - openCydURL(url); + if (isAppReady) { + openCydURL(url); + } else { + cydURLQueue.push(url); + } }) // Initialize the logger @@ -210,8 +225,17 @@ async function createWindow() { icon: icon, }); - // Handle power monitor events + // Mark the app as ready + isAppReady = true; + + // Handle any cyd:// URLs that came in before the app was ready + setTimeout(() => { + for (const url of cydURLQueue) { + openCydURL(url); + } + }, 100); + // Handle power monitor events powerMonitor.on('suspend', () => { log.info('System is suspending'); try { @@ -545,11 +569,6 @@ if (!app.requestSingleInstanceLock()) { openCydURL(cydURL); } }) - - // Initialize the app - app.whenReady().then(async () => { - await initializeApp(); - }) } app.enableSandbox(); @@ -561,10 +580,10 @@ app.on('window-all-closed', () => { } }); -app.on('activate', () => { +app.on('activate', async () => { // On OS X it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (BrowserWindow.getAllWindows().length === 0) { - createWindow(); + await createWindow(); } }); From c0fd8bcca855ae40ca222e1b3209a97ae568ae11 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 12:35:41 -0800 Subject: [PATCH 05/20] Open the app with cyd:// path / --- src/main.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main.ts b/src/main.ts index dc70ed3c..a02adea5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -102,10 +102,9 @@ const openCydURL = async (cydURL: string) => { return; } - // Handle /open - if (url.pathname == "/open") { + // Handle / (which just opens the app) + if (url.pathname == "/") { // Supported! Do nothing, since the app should now be opened - log.info('protocol handler opening app'); } // For all other paths, show an error else { From 61582ab7a4a0bf9561dd65a39ef8cffb661fc073 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 12:44:46 -0800 Subject: [PATCH 06/20] Update packaging to support cyd:// and cyd-dev:// URLs --- forge.config.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/forge.config.ts b/forge.config.ts index 7cead5b8..7ec5425c 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -92,6 +92,21 @@ function removeCodeSignatures(dir: string) { }); } +// For cyd:// and cyd-dev:// URLs +const protocols = []; +if (process.env.CYD_ENV == 'prod') { + protocols.push({ + "name": "Cyd", + "schemes": ["cyd"] + }); +} else { + protocols.push({ + "name": "Cyd Dev", + "schemes": ["cyd-dev"] + }); +} +const mimeTypeScheme = process.env.CYD_ENV == 'prod' ? 'x-scheme-handler/cyd' : 'x-scheme-handler/cyd-dev'; + const config: ForgeConfig = { packagerConfig: { name: process.env.CYD_ENV == 'prod' ? 'Cyd' : 'Cyd Dev', @@ -115,6 +130,7 @@ const config: ForgeConfig = { path.join(buildPath, 'config.json'), path.join(assetsPath, 'icon.png'), ], + protocols: protocols, }, rebuildConfig: {}, makers: [ @@ -165,6 +181,7 @@ const config: ForgeConfig = { productName: process.env.CYD_ENV == 'prod' ? "Cyd" : "Cyd Dev", bin: process.env.CYD_ENV == 'prod' ? "cyd" : "cyd-dev", name: process.env.CYD_ENV == 'prod' ? "cyd" : "cyd-dev", + mimeType: [mimeTypeScheme], }), // Linux Debian new MakerDeb({ @@ -177,6 +194,7 @@ const config: ForgeConfig = { productName: process.env.CYD_ENV == 'prod' ? "Cyd" : "Cyd Dev", bin: process.env.CYD_ENV == 'prod' ? "cyd" : "cyd-dev", name: process.env.CYD_ENV == 'prod' ? "cyd" : "cyd-dev", + mimeType: [mimeTypeScheme], } }) ], From 7cd8530b104a65c5eb3729431209da86b0caca77 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 14:09:52 -0800 Subject: [PATCH 07/20] Move logging to the top of main.ts --- src/main.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main.ts b/src/main.ts index a02adea5..eb812bf3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -32,6 +32,12 @@ import { packageExceptionForReport } from './util'; +// Initialize the logger +log.initialize(); +log.transports.file.level = false; // Disable file logging +log.info('Cyd version:', app.getVersion()); +log.info('User data folder is at:', app.getPath('userData')); + declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string; declare const MAIN_WINDOW_VITE_NAME: string; @@ -71,7 +77,6 @@ if (require('electron-squirrel-startup')) { app.quit(); } - // Handle cyd:// URLs (or cyd-dev:// in dev mode) // See: https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app if (process.defaultApp) { @@ -125,12 +130,6 @@ app.on('open-url', (event, url) => { } }) -// Initialize the logger -log.initialize(); -log.transports.file.level = false; // Disable file logging -log.info('Cyd version:', app.getVersion()); -log.info('User data folder is at:', app.getPath('userData')); - const cydDevMode = process.env.CYD_DEV === "1"; async function initializeApp() { From 5a2d45e504c76fc94538e54b47a2cab3f7751cf7 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 14:20:58 -0800 Subject: [PATCH 08/20] Actually, move logging to below loading config and setting the app name --- src/main.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main.ts b/src/main.ts index eb812bf3..09901929 100644 --- a/src/main.ts +++ b/src/main.ts @@ -32,12 +32,6 @@ import { packageExceptionForReport } from './util'; -// Initialize the logger -log.initialize(); -log.transports.file.level = false; // Disable file logging -log.info('Cyd version:', app.getVersion()); -log.info('User data folder is at:', app.getPath('userData')); - declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string; declare const MAIN_WINDOW_VITE_NAME: string; @@ -77,6 +71,12 @@ if (require('electron-squirrel-startup')) { app.quit(); } +// Initialize the logger +log.initialize(); +log.transports.file.level = false; // Disable file logging +log.info('Cyd version:', app.getVersion()); +log.info('User data folder is at:', app.getPath('userData')); + // Handle cyd:// URLs (or cyd-dev:// in dev mode) // See: https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app if (process.defaultApp) { From 29b255e047627a1b9ebe640b0bc5c9c6ae490afc Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 14:23:16 -0800 Subject: [PATCH 09/20] Add a debug log --- src/main.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main.ts b/src/main.ts index 09901929..505b138f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -227,11 +227,10 @@ async function createWindow() { isAppReady = true; // Handle any cyd:// URLs that came in before the app was ready - setTimeout(() => { - for (const url of cydURLQueue) { - openCydURL(url); - } - }, 100); + log.debug('Handling cyd:// URLs in queue:', cydURLQueue); + for (const url of cydURLQueue) { + openCydURL(url); + } // Handle power monitor events powerMonitor.on('suspend', () => { From c7ac8f2461a81918ac2df886750b7194c21abbc3 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 14:24:19 -0800 Subject: [PATCH 10/20] Automatically log to disk when running not prod --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 505b138f..d1c5eb16 100644 --- a/src/main.ts +++ b/src/main.ts @@ -73,7 +73,7 @@ if (require('electron-squirrel-startup')) { // Initialize the logger log.initialize(); -log.transports.file.level = false; // Disable file logging +log.transports.file.level = config.mode == "prod" ? false : "debug"; // Disable file logging in prod mode log.info('Cyd version:', app.getVersion()); log.info('User data folder is at:', app.getPath('userData')); From fcd78d4fc8bf881224050c2f1fdd3aae3173d00d Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 14:31:30 -0800 Subject: [PATCH 11/20] Add Cyd URLs to queue from both open-url event (macOS) and second-instance event (Windows, Linux) --- src/main.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main.ts b/src/main.ts index d1c5eb16..e66ca82a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -88,8 +88,14 @@ if (process.defaultApp) { } const openCydURL = async (cydURL: string) => { + if (!isAppReady) { + log.debug('Adding cyd:// URL to queue:', cydURL); + cydURLQueue.push(cydURL); + return; + } + const url = new URL(cydURL); - log.info(`Received ${url.protocol}:// URL: ${url.toString()}`); + log.info(`Opening URL: ${url.toString()}`); // If there's no main window, open one if (BrowserWindow.getAllWindows().length === 0) { @@ -123,11 +129,7 @@ const openCydURL = async (cydURL: string) => { } app.on('open-url', (event, url) => { - if (isAppReady) { - openCydURL(url); - } else { - cydURLQueue.push(url); - } + openCydURL(url); }) const cydDevMode = process.env.CYD_DEV === "1"; From 5287dbed5ac8fa75577c66fad424a8d50941a13a Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 15:06:18 -0800 Subject: [PATCH 12/20] If the last arg is cyd:// URL, pass it into app.setAsDefaultProtocolClient --- src/main.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main.ts b/src/main.ts index e66ca82a..b6046e56 100644 --- a/src/main.ts +++ b/src/main.ts @@ -78,11 +78,10 @@ log.info('Cyd version:', app.getVersion()); log.info('User data folder is at:', app.getPath('userData')); // Handle cyd:// URLs (or cyd-dev:// in dev mode) -// See: https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app -if (process.defaultApp) { - if (process.argv.length >= 2) { - app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev", process.execPath, [path.resolve(process.argv[1])]) - } +const protocolString = config.mode == "prod" ? "cyd" : "cyd-dev"; +const lastArg = process.argv.length >= 2 ? process.argv[process.argv.length - 1] : ""; +if (lastArg.startsWith(protocolString + "://")) { + app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev", process.execPath, [lastArg]) } else { app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev") } From edc21d73895115286ca7b497f41c261cc2690b3c Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 15:15:03 -0800 Subject: [PATCH 13/20] Try manually calling initial openCydURL in Windows and Linux if neccessary --- src/main.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main.ts b/src/main.ts index b6046e56..1a475041 100644 --- a/src/main.ts +++ b/src/main.ts @@ -78,14 +78,6 @@ log.info('Cyd version:', app.getVersion()); log.info('User data folder is at:', app.getPath('userData')); // Handle cyd:// URLs (or cyd-dev:// in dev mode) -const protocolString = config.mode == "prod" ? "cyd" : "cyd-dev"; -const lastArg = process.argv.length >= 2 ? process.argv[process.argv.length - 1] : ""; -if (lastArg.startsWith(protocolString + "://")) { - app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev", process.execPath, [lastArg]) -} else { - app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev") -} - const openCydURL = async (cydURL: string) => { if (!isAppReady) { log.debug('Adding cyd:// URL to queue:', cydURL); @@ -127,6 +119,21 @@ const openCydURL = async (cydURL: string) => { } } +// Register the cyd:// (or cyd-dev://) protocol +const protocolString = config.mode == "prod" ? "cyd" : "cyd-dev"; +const lastArg = process.argv.length >= 2 ? process.argv[process.argv.length - 1] : ""; +if (lastArg.startsWith(protocolString + "://")) { + app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev", process.execPath, [lastArg]) +} else { + app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev") +} + +// In Linux in Windows, handle the cyd:// URL that was passed in +if ((process.platform == 'linux' || process.platform == 'win32') && lastArg.startsWith(protocolString + "://")) { + openCydURL(lastArg); +} + +// Handle the cyd:// URL that was passed in on macOS app.on('open-url', (event, url) => { openCydURL(url); }) From 2742eb90a0c3f509e90714cd5a461b820817e32e Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 15:46:20 -0800 Subject: [PATCH 14/20] Try adding protocol.registerSchemesAsPrivileged to see if Windows will launch Cyd on a cyd:// URL --- src/main.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main.ts b/src/main.ts index 1a475041..f6c9d89b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import { BrowserWindow, ipcMain, dialog, + protocol, shell, webContents, nativeImage, @@ -128,6 +129,19 @@ if (lastArg.startsWith(protocolString + "://")) { app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev") } +// Also register the protocol handler this way, as it seems to be required for Windows +protocol.registerSchemesAsPrivileged([ + { + scheme: protocolString, + privileges: { + standard: true, + secure: true, + supportFetchAPI: true, + corsEnabled: true, + }, + }, +]); + // In Linux in Windows, handle the cyd:// URL that was passed in if ((process.platform == 'linux' || process.platform == 'win32') && lastArg.startsWith(protocolString + "://")) { openCydURL(lastArg); From a3e61c9b735d1cd4a9d597997f4ee0b16655f0f9 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 16:03:25 -0800 Subject: [PATCH 15/20] Remove protocol.registerSchemesAsPrivileged because it does not help the Windows situation --- src/main.ts | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/main.ts b/src/main.ts index f6c9d89b..d396e21d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -122,32 +122,15 @@ const openCydURL = async (cydURL: string) => { // Register the cyd:// (or cyd-dev://) protocol const protocolString = config.mode == "prod" ? "cyd" : "cyd-dev"; -const lastArg = process.argv.length >= 2 ? process.argv[process.argv.length - 1] : ""; -if (lastArg.startsWith(protocolString + "://")) { - app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev", process.execPath, [lastArg]) -} else { - app.setAsDefaultProtocolClient(config.mode == "prod" ? "cyd" : "cyd-dev") -} +app.setAsDefaultProtocolClient(protocolString) -// Also register the protocol handler this way, as it seems to be required for Windows -protocol.registerSchemesAsPrivileged([ - { - scheme: protocolString, - privileges: { - standard: true, - secure: true, - supportFetchAPI: true, - corsEnabled: true, - }, - }, -]); - -// In Linux in Windows, handle the cyd:// URL that was passed in +// In Linux in Windows, handle cyd:// URLs passed in via the CLI +const lastArg = process.argv.length >= 2 ? process.argv[process.argv.length - 1] : ""; if ((process.platform == 'linux' || process.platform == 'win32') && lastArg.startsWith(protocolString + "://")) { openCydURL(lastArg); } -// Handle the cyd:// URL that was passed in on macOS +// In macOS, handle the cyd:// URLs app.on('open-url', (event, url) => { openCydURL(url); }) From 0bcf611e5b8160c1f862ebc06282715b39e4c8a4 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 16:05:59 -0800 Subject: [PATCH 16/20] Fix lint --- src/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index d396e21d..940f4f0a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,7 +9,6 @@ import { BrowserWindow, ipcMain, dialog, - protocol, shell, webContents, nativeImage, From a1fa1cc41171fbfc02938bc32eae57226118ba2d Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Jan 2025 16:19:27 -0800 Subject: [PATCH 17/20] Set logging to debug for everything but prod --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 940f4f0a..e784c694 100644 --- a/src/main.ts +++ b/src/main.ts @@ -153,7 +153,7 @@ async function initializeApp() { } // Set the log level - if (config.mode == "dev" || config.mode == "local") { + if (config.mode != "prod") { log.transports.console.level = "debug"; } else { log.transports.console.level = "info"; From 0a7853917ae8d86568359dc210611e5357d8a1a8 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 21 Jan 2025 08:57:04 -0800 Subject: [PATCH 18/20] Update src/main.ts Co-authored-by: Saptak Sengupta --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index e784c694..3df007e5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -123,7 +123,7 @@ const openCydURL = async (cydURL: string) => { const protocolString = config.mode == "prod" ? "cyd" : "cyd-dev"; app.setAsDefaultProtocolClient(protocolString) -// In Linux in Windows, handle cyd:// URLs passed in via the CLI +// In Linux and Windows, handle cyd:// URLs passed in via the CLI const lastArg = process.argv.length >= 2 ? process.argv[process.argv.length - 1] : ""; if ((process.platform == 'linux' || process.platform == 'win32') && lastArg.startsWith(protocolString + "://")) { openCydURL(lastArg); From 77b14169a0559adc2f7bbe8f8b576955f96a2389 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 21 Jan 2025 09:44:40 -0800 Subject: [PATCH 19/20] Skip code signing when making a macOS build that will not be published --- forge.config.ts | 4 ++-- scripts/make-dev-macos.sh | 1 + scripts/make-local-macos.sh | 1 + scripts/make-prod-macos.sh | 1 + scripts/publish-dev-macos.sh | 1 + scripts/publish-prod-macos.sh | 1 + 6 files changed, 7 insertions(+), 2 deletions(-) diff --git a/forge.config.ts b/forge.config.ts index 7ec5425c..af7d5083 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -233,7 +233,7 @@ const config: ForgeConfig = { // macOS codesign here because osxSign seems totally broken preMake: async (_forgeConfig) => { - if (os.platform() !== 'darwin') { + if (os.platform() !== 'darwin' || process.env.MACOS_RELEASE !== 'true') { return; } @@ -303,7 +303,7 @@ const config: ForgeConfig = { // macOS notarize here because osxNotarize is broken without using osxSign postMake: async (forgeConfig, makeResults) => { - if (makeResults[0].platform !== 'darwin') { + if (makeResults[0].platform !== 'darwin' || process.env.MACOS_RELEASE !== 'true') { return makeResults; } diff --git a/scripts/make-dev-macos.sh b/scripts/make-dev-macos.sh index 0c9d97dd..2e5c7032 100755 --- a/scripts/make-dev-macos.sh +++ b/scripts/make-dev-macos.sh @@ -1,6 +1,7 @@ #!/bin/sh export CYD_ENV=dev export DEBUG=electron-packager,electron-universal,electron-forge*,electron-installer* +export MACOS_RELEASE=false ./scripts/clean.sh electron-forge make --arch universal \ No newline at end of file diff --git a/scripts/make-local-macos.sh b/scripts/make-local-macos.sh index ec476a96..ae247839 100755 --- a/scripts/make-local-macos.sh +++ b/scripts/make-local-macos.sh @@ -1,6 +1,7 @@ #!/bin/sh export CYD_ENV=local export DEBUG=electron-packager,electron-universal,electron-forge*,electron-installer* +export MACOS_RELEASE=false ./scripts/clean.sh electron-forge make --arch universal \ No newline at end of file diff --git a/scripts/make-prod-macos.sh b/scripts/make-prod-macos.sh index 190e6ee5..4859837e 100755 --- a/scripts/make-prod-macos.sh +++ b/scripts/make-prod-macos.sh @@ -1,6 +1,7 @@ #!/bin/sh export CYD_ENV=prod export DEBUG=electron-packager,electron-universal,electron-forge*,electron-installer* +export MACOS_RELEASE=false ./scripts/clean.sh electron-forge make --arch universal \ No newline at end of file diff --git a/scripts/publish-dev-macos.sh b/scripts/publish-dev-macos.sh index cf71ee32..2e52c839 100755 --- a/scripts/publish-dev-macos.sh +++ b/scripts/publish-dev-macos.sh @@ -1,6 +1,7 @@ #!/bin/sh export CYD_ENV=dev export DEBUG=electron-packager,electron-universal,electron-forge*,electron-installer* +export MACOS_RELEASE=true ./scripts/clean.sh electron-forge publish --arch universal \ No newline at end of file diff --git a/scripts/publish-prod-macos.sh b/scripts/publish-prod-macos.sh index 491a07b3..2cb27388 100755 --- a/scripts/publish-prod-macos.sh +++ b/scripts/publish-prod-macos.sh @@ -1,6 +1,7 @@ #!/bin/sh export CYD_ENV=prod export DEBUG=electron-packager,electron-universal,electron-forge*,electron-installer* +export MACOS_RELEASE=true ./scripts/clean.sh electron-forge publish --arch universal \ No newline at end of file From 9acc031ca353886b0504ee83ce86c7420db2aa91 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 21 Jan 2025 09:49:22 -0800 Subject: [PATCH 20/20] Support the two Cyd URLs cyd://open/ and cyd://social.cyd.api/atproto-oath-callback (and their cyd-dev equivalents) --- src/main.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main.ts b/src/main.ts index e784c694..1afd493d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -93,30 +93,30 @@ const openCydURL = async (cydURL: string) => { await createWindow(); } - // Validate the hostname - const validHostname = config.mode == "prod" ? 'social.cyd.api' : 'social.cyd.dev-api'; - if (url.hostname != validHostname) { - dialog.showMessageBoxSync({ - title: "Cyd", - message: `Invalid hostname in URL ${url.toString()}. I'm expecting "${validHostname}" as the hostname.`, - type: 'info', - }); + // If hostname is "open", this just means open Cyd + if (url.hostname == "open") { + // Success! return; } - // Handle / (which just opens the app) - if (url.pathname == "/") { - // Supported! Do nothing, since the app should now be opened - } - // For all other paths, show an error - else { + // Check for Bluesky OAuth redirect + const blueskyHostname = config.mode == "prod" ? 'social.cyd.api' : 'social.cyd.dev-api'; + if (url.hostname == blueskyHostname && url.pathname == "/atproto-oauth-callback") { dialog.showMessageBoxSync({ title: "Cyd", - message: `Invalid Cyd URL: ${url.toString()}.`, + message: `Bluesky OAuth is not implemented yet.`, type: 'info', }); return; } + + // For all other paths, show an error + dialog.showMessageBoxSync({ + title: "Cyd", + message: `Invalid Cyd URL: ${url.toString()}.`, + type: 'info', + }); + return; } // Register the cyd:// (or cyd-dev://) protocol