From be8109169896b3317697399558e15a4e13e81038 Mon Sep 17 00:00:00 2001
From: Shazib Hussain <me@shazib.com>
Date: Sun, 4 Jun 2023 21:21:15 +0100
Subject: [PATCH] More Electron fixes (#1099)

- Fix socket connection issues when reloading
- Fix external url clicking & middle clicking internal links
- Remove broken menu option. Easier for now than refactoring the
settings panel it now lives in. We can add it back later if needed?
---
 packages/desktop-electron/index.js            | 23 +++++++++++++++++++
 packages/desktop-electron/menu.js             |  9 --------
 .../client/fetch/__mocks__/index.web.ts       |  2 +-
 .../platform/client/fetch/index.browser.ts    |  4 ++++
 .../src/platform/client/fetch/index.d.ts      |  2 +-
 .../src/platform/client/fetch/index.web.ts    | 20 ++++++++++++----
 .../server/connection/index.electron.ts       | 13 +++++++++++
 upcoming-release-notes/1099.md                |  6 +++++
 8 files changed, 64 insertions(+), 15 deletions(-)
 create mode 100644 upcoming-release-notes/1099.md

diff --git a/packages/desktop-electron/index.js b/packages/desktop-electron/index.js
index e0d3079ed..7d5808e3b 100644
--- a/packages/desktop-electron/index.js
+++ b/packages/desktop-electron/index.js
@@ -165,6 +165,25 @@ async function createWindow() {
     win.webContents.send('set-socket', { name: serverSocket });
   });
 
+  // hit when middle-clicking buttons or <a href/> with a target set to _blank
+  // always deny, optionally redirect to browser
+  win.webContents.setWindowOpenHandler(({ url }) => {
+    if (isExternalUrl(url)) {
+      shell.openExternal(url);
+    }
+
+    return { action: 'deny' };
+  });
+
+  // hit when clicking <a href/> with no target
+  // optionally redirect to browser
+  win.webContents.on('will-navigate', (event, url) => {
+    if (isExternalUrl(url)) {
+      shell.openExternal(url);
+      event.preventDefault();
+    }
+  });
+
   if (process.platform === 'win32') {
     Menu.setApplicationMenu(null);
     win.setMenu(getMenu(isDev, createWindow));
@@ -175,6 +194,10 @@ async function createWindow() {
   clientWin = win;
 }
 
+function isExternalUrl(url) {
+  return !url.includes('localhost:') && !url.includes('app://');
+}
+
 function updateMenu(isBudgetOpen) {
   const menu = getMenu(isDev, createWindow);
   const file = menu.items.filter(item => item.label === 'File')[0];
diff --git a/packages/desktop-electron/menu.js b/packages/desktop-electron/menu.js
index e71da500d..9c8e371e7 100644
--- a/packages/desktop-electron/menu.js
+++ b/packages/desktop-electron/menu.js
@@ -149,15 +149,6 @@ function getMenu(isDev, createWindow) {
             );
           },
         },
-        {
-          label: 'Repair split transactions',
-          enabled: false,
-          click: function (menuItem, focusedWin) {
-            focusedWin.webContents.executeJavaScript(
-              '__history && __history.push("/tools/fix-splits", { locationPtr: __history.location })',
-            );
-          },
-        },
       ],
     },
     {
diff --git a/packages/loot-core/src/platform/client/fetch/__mocks__/index.web.ts b/packages/loot-core/src/platform/client/fetch/__mocks__/index.web.ts
index 40cc73c0d..b486938ce 100644
--- a/packages/loot-core/src/platform/client/fetch/__mocks__/index.web.ts
+++ b/packages/loot-core/src/platform/client/fetch/__mocks__/index.web.ts
@@ -22,7 +22,7 @@ export const initServer: T.InitServer = handlers => {
   };
 };
 
-export const clearServer: T.ClearServer = () => {
+export const clearServer: T.ClearServer = async () => {
   serverHandler = null;
   listeners = new Map();
 };
diff --git a/packages/loot-core/src/platform/client/fetch/index.browser.ts b/packages/loot-core/src/platform/client/fetch/index.browser.ts
index 9f4c3a89c..ee81c1c4c 100644
--- a/packages/loot-core/src/platform/client/fetch/index.browser.ts
+++ b/packages/loot-core/src/platform/client/fetch/index.browser.ts
@@ -188,3 +188,7 @@ export const listen: T.Listen = function (name, cb) {
 export const unlisten: T.Unlisten = function (name) {
   listeners.set(name, []);
 };
+
+export const clearServer: T.ClearServer = async function () {
+  //
+};
diff --git a/packages/loot-core/src/platform/client/fetch/index.d.ts b/packages/loot-core/src/platform/client/fetch/index.d.ts
index ae2b1aeb9..5ae4f2d3e 100644
--- a/packages/loot-core/src/platform/client/fetch/index.d.ts
+++ b/packages/loot-core/src/platform/client/fetch/index.d.ts
@@ -33,5 +33,5 @@ export type InitServer = typeof initServer;
 export function serverPush(name: string, args: unknown): void;
 export type ServerPush = typeof serverPush;
 
-export function clearServer(): void;
+export async function clearServer(): void;
 export type ClearServer = typeof clearServer;
diff --git a/packages/loot-core/src/platform/client/fetch/index.web.ts b/packages/loot-core/src/platform/client/fetch/index.web.ts
index 0b27c4309..e9427c7c7 100644
--- a/packages/loot-core/src/platform/client/fetch/index.web.ts
+++ b/packages/loot-core/src/platform/client/fetch/index.web.ts
@@ -72,13 +72,10 @@ function connectSocket(port, onOpen) {
 
     onOpen();
   };
-
-  client.onclose = event => {
-    socketClient = null;
-  };
 }
 
 export const init: T.Init = async function (socketName) {
+  await clearServer();
   return new Promise(resolve => connectSocket(socketName, resolve));
 };
 
@@ -141,3 +138,18 @@ export const listen: T.Listen = function (name, cb) {
 export const unlisten: T.Unlisten = function (name) {
   listeners.set(name, []);
 };
+
+async function closeSocket(onClose) {
+  socketClient.onclose = event => {
+    socketClient = null;
+    onClose();
+  };
+
+  await socketClient.close();
+}
+
+export const clearServer: T.ClearServer = async function () {
+  if (socketClient != null) {
+    return new Promise(closeSocket);
+  }
+};
diff --git a/packages/loot-core/src/platform/server/connection/index.electron.ts b/packages/loot-core/src/platform/server/connection/index.electron.ts
index bf8c22a9d..faf0ffea5 100644
--- a/packages/loot-core/src/platform/server/connection/index.electron.ts
+++ b/packages/loot-core/src/platform/server/connection/index.electron.ts
@@ -22,13 +22,23 @@ export const init: T.Init = function (socketName, handlers) {
 
   // websockets doesn't support sending objects so parse/stringify needed
   wss.on('connection', function connection(ws) {
+    ws.on('error', console.error);
+
     ws.on('message', data => {
       let msg = JSON.parse(data);
+
+      if (ws.readyState !== 1) {
+        return;
+      }
+
       let { id, name, args, undoTag, catchErrors } = msg;
 
       if (handlers[name]) {
         runHandler(handlers[name], args, { undoTag, name }).then(
           result => {
+            if (ws.readyState !== 1) {
+              return;
+            }
             if (catchErrors) {
               result = { data: result, error: null };
             }
@@ -47,6 +57,9 @@ export const init: T.Init = function (socketName, handlers) {
             );
           },
           nativeError => {
+            if (ws.readyState !== 1) {
+              return;
+            }
             let error = coerceError(nativeError);
 
             if (name.startsWith('api/')) {
diff --git a/upcoming-release-notes/1099.md b/upcoming-release-notes/1099.md
new file mode 100644
index 000000000..ab8ccec2b
--- /dev/null
+++ b/upcoming-release-notes/1099.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [Shazib]
+---
+
+Fix reloading issues, external url handling, and tidy up menus in the electron app. 
\ No newline at end of file
-- 
GitLab