From 565bf34bde03c90d21990c1ae912d85eeba8e560 Mon Sep 17 00:00:00 2001 From: Elvanos Date: Wed, 13 Sep 2023 22:37:05 +0200 Subject: [PATCH] added app splash screen, fixed multi-instance bug --- index.html | 36 +++++++ interfaces/I_faWindowControlAPI.ts | 5 + package.json | 2 +- quasar.config.js | 94 +++++++++---------- .../faWindowControlAPI.ts | 8 ++ src-electron/mainScripts/appManagement.ts | 6 +- .../mainScripts/mainWindowCreation.ts | 43 ++++++++- 7 files changed, 136 insertions(+), 58 deletions(-) diff --git a/index.html b/index.html index 3c8c78f..ea7700b 100644 --- a/index.html +++ b/index.html @@ -14,8 +14,44 @@ + +
+ +
diff --git a/interfaces/I_faWindowControlAPI.ts b/interfaces/I_faWindowControlAPI.ts index 1292783..f5baba8 100644 --- a/interfaces/I_faWindowControlAPI.ts +++ b/interfaces/I_faWindowControlAPI.ts @@ -10,6 +10,11 @@ export interface I_faWindowControlAPI { */ minimizeWindow: () => void + /** + * Mazimizes the current window + */ + maximizeWindow: () => void + /** * Resizes the current window. * - If the window is maximized, smallifies it diff --git a/package.json b/package.json index 05fe922..b06e9ff 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "scripts": { "lint": "eslint --ext .js,.ts,.vue ./", "dev:electron": "quasar dev -m electron", - "build": "quasar build -m electron --publish never --debug", + "build": "quasar build -m electron --publish never", "test:unit:ui": "vitest --ui", "test:unit:ci": "vitest run --reporter verbose", "test:component": "node \"node_modules/@playwright/test/cli.js\" test src/components/", diff --git a/quasar.config.js b/quasar.config.js index 9bd03cd..379215a 100644 --- a/quasar.config.js +++ b/quasar.config.js @@ -8,8 +8,8 @@ // Configuration for your app // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js -const { configure } = require('quasar/wrappers') -const path = require('path') +const { configure } = require("quasar/wrappers"); +const path = require("path"); module.exports = configure(function (/* ctx */) { return { @@ -19,7 +19,7 @@ module.exports = configure(function (/* ctx */) { // exclude: [], // rawOptions: {}, warnings: true, - errors: true + errors: true, }, // https://v2.quasar.dev/quasar-cli-vite/prefetch-feature @@ -28,38 +28,33 @@ module.exports = configure(function (/* ctx */) { // app boot file (/src/boot) // --> boot files are part of "main.js" // https://v2.quasar.dev/quasar-cli-vite/boot-files - boot: [ - 'i18n', - 'axios' - ], + boot: ["i18n", "axios"], // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css - css: [ - 'app.scss' - ], + css: ["app.scss"], // https://github.com/quasarframework/quasar/tree/dev/extras extras: [ // 'ionicons-v4', - 'mdi-v5', - 'fontawesome-v6', + "mdi-v5", + "fontawesome-v6", // 'eva-icons', // 'themify', // 'line-awesome', // 'roboto-font', // optional, you are not bound to it - 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! - 'material-icons' // optional, you are not bound to it + "roboto-font-latin-ext", // this or either 'roboto-font', NEVER both! + "material-icons", // optional, you are not bound to it ], // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build build: { target: { - browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'], - node: 'node16' + browser: ["es2019", "edge88", "firefox78", "chrome87", "safari13.1"], + node: "node16", }, - vueRouterMode: 'history', // available values: 'hash', 'history' + vueRouterMode: "history", // available values: 'hash', 'history' // vueRouterBase, // vueDevtools, // vueOptionsAPI: false, @@ -79,24 +74,27 @@ module.exports = configure(function (/* ctx */) { // viteVuePluginOptions: {}, vitePlugins: [ - ['@intlify/vite-plugin-vue-i18n', { - // if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false` - // compositionOnly: false, + [ + "@intlify/vite-plugin-vue-i18n", + { + // if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false` + // compositionOnly: false, - // if you want to use named tokens in your Vue I18n messages, such as 'Hello {name}', - // you need to set `runtimeOnly: false` - // runtimeOnly: false, + // if you want to use named tokens in your Vue I18n messages, such as 'Hello {name}', + // you need to set `runtimeOnly: false` + // runtimeOnly: false, - // you need to set i18n resource including paths ! - include: path.resolve(__dirname, './src/i18n/**') - }] - ] + // you need to set i18n resource including paths ! + include: path.resolve(__dirname, "./src/i18n/**"), + }, + ], + ], }, // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer devServer: { // https: true - open: true // opens browser window automatically + open: true, // opens browser window automatically }, // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework @@ -114,12 +112,12 @@ module.exports = configure(function (/* ctx */) { // directives: [], // Quasar plugins - plugins: [] + plugins: [], }, // animations: 'all', // --- includes all animations // https://v2.quasar.dev/options/animations - animations: [], + animations: "all", // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#sourcefiles // sourceFiles: { @@ -150,17 +148,17 @@ module.exports = configure(function (/* ctx */) { // (gets superseded if process.env.PORT is specified at runtime) middlewares: [ - 'render' // keep this as last one - ] + "render", // keep this as last one + ], }, // https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa pwa: { - workboxMode: 'generateSW', // or 'injectManifest' + workboxMode: "generateSW", // or 'injectManifest' injectPwaMetaTags: true, - swFilename: 'sw.js', - manifestFilename: 'manifest.json', - useCredentialsForManifestTag: false + swFilename: "sw.js", + manifestFilename: "manifest.json", + useCredentialsForManifestTag: false, // useFilenameHashes: true, // extendGenerateSWOptions (cfg) {} // extendInjectManifestOptions (cfg) {}, @@ -175,7 +173,7 @@ module.exports = configure(function (/* ctx */) { // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor capacitor: { - hideSplashscreen: true + hideSplashscreen: true, }, // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron @@ -185,17 +183,15 @@ module.exports = configure(function (/* ctx */) { inspectPort: 5858, - bundler: 'builder', // 'packager' or 'builder' + bundler: "builder", // 'packager' or 'builder' packager: { // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options - // OS X / Mac App Store // appBundleId: '', // appCategoryType: '', // osxSign: '', // protocol: 'myapp://path', - // Windows only // win32metadata: { ... } }, @@ -203,21 +199,19 @@ module.exports = configure(function (/* ctx */) { builder: { // https://www.electron.build/configuration/configuration - appId: 'fantasia-archive', + appId: "fantasia-archive", win: { - icon: 'src-electron/icons/icon.ico' - } - } + icon: "src-electron/icons/icon.ico", + }, + }, }, // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex bex: { - contentScripts: [ - 'my-content-script' - ] + contentScripts: ["my-content-script"], // extendBexScriptsConf (esbuildConf) {} // extendBexManifestJson (json) {} - } - } -}) + }, + }; +}); diff --git a/src-electron/customContentBridgeAPIs/faWindowControlAPI.ts b/src-electron/customContentBridgeAPIs/faWindowControlAPI.ts index 83eb6ee..8ffaeb9 100644 --- a/src-electron/customContentBridgeAPIs/faWindowControlAPI.ts +++ b/src-electron/customContentBridgeAPIs/faWindowControlAPI.ts @@ -19,6 +19,14 @@ export const faWindowControlAPI: I_faWindowControlAPI = { } }, + maximizeWindow () { + const currentWindow = BrowserWindow.getFocusedWindow() + + if (currentWindow !== null) { + currentWindow.maximize() + } + }, + resizeWindow () { const currentWindow = BrowserWindow.getFocusedWindow() diff --git a/src-electron/mainScripts/appManagement.ts b/src-electron/mainScripts/appManagement.ts index 4e2db2a..2a3666e 100644 --- a/src-electron/mainScripts/appManagement.ts +++ b/src-electron/mainScripts/appManagement.ts @@ -16,11 +16,9 @@ export const openAppWindowManager = () => { // Create the app window in the normal way app.whenReady().then(mainWindowCreation) - // Create the app window, if it still doesn't exist yet + // Create the app window app.on('activate', () => { - if (app.requestSingleInstanceLock()) { - mainWindowCreation() - } + mainWindowCreation() }) } diff --git a/src-electron/mainScripts/mainWindowCreation.ts b/src-electron/mainScripts/mainWindowCreation.ts index 9117592..2593f0a 100644 --- a/src-electron/mainScripts/mainWindowCreation.ts +++ b/src-electron/mainScripts/mainWindowCreation.ts @@ -1,7 +1,34 @@ -import { BrowserWindow } from 'electron' +import { BrowserWindow, app } from 'electron' import { enable } from '@electron/remote/main' import path from 'path' +/** + * Prevent app to launch a secondary instance + */ +const preventSecondaryAppInstance = (appWindow: BrowserWindow | undefined) => { + /** + * Determines if the app is the primary instance + * - This exists as a variable due to the app bugging out if used directly from "app" + */ + const isPrimaryInstance = app.requestSingleInstanceLock() + + // Check this is NOT the primary app instance + if (!isPrimaryInstance) { + app.quit() + } else { + /* + Someone tried to start a second instance. That one is closed already. + Our instance here (the first instance) maximizes it's own window and refocuses it. + */ + app.on('second-instance', () => { + if (appWindow) { + if (appWindow.isMinimized()) appWindow.restore() + appWindow.focus() + } + }) + } +} + /** * Creates the main app window */ @@ -12,6 +39,7 @@ export const mainWindowCreation = () => { let appWindow: BrowserWindow | undefined = new BrowserWindow({ useContentSize: true, frame: false, + show: false, icon: path.resolve(__dirname, '../icons/icon.png'), webPreferences: { sandbox: false, @@ -23,9 +51,15 @@ export const mainWindowCreation = () => { // Enable actual webContents inside the created window enable(appWindow.webContents) - // Set the current window as empty and maximize it + appWindow.once('ready-to-show', () => { + if (appWindow) { + appWindow.maximize() + appWindow.focus() + } + }) + + // Set the current window's menu as empty appWindow.setMenu(null) - appWindow.maximize() // Load the basic app URL appWindow.loadURL(process.env.APP_URL) @@ -39,4 +73,7 @@ export const mainWindowCreation = () => { appWindow.on('closed', () => { appWindow = undefined }) + + // Check if we are on the primary or secondary instance of the app + preventSecondaryAppInstance(appWindow) }