added app splash screen, fixed multi-instance bug

This commit is contained in:
Elvanos 2023-09-13 22:37:05 +02:00
parent 2e722bd8af
commit 565bf34bde
7 changed files with 136 additions and 58 deletions

View file

@ -14,8 +14,44 @@
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
<link rel="icon" type="image/ico" href="favicon.ico">
<style>
.splashLoadingWrapper{
background-color: #18303a;
overflow: hidden;
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
transition: 0.5s opacity ease-in;
transition-delay: 2s;
z-index: 9999999999999999999;
}
.splashLoading{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
.splashLoading path{
fill: #d7ac47;
}
#q-app[data-v-app] ~ .splashLoadingWrapper{
opacity: 0 !important;
user-select: none !important;
pointer-events: none !important;
}
</style>
</head>
<body>
<!-- quasar:entry-point -->
<div class="splashLoadingWrapper">
<svg class="splashLoading" width="500px" height="500px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-20,-20)"><path d="M79.9,52.6C80,51.8,80,50.9,80,50s0-1.8-0.1-2.6l-5.1-0.4c-0.3-2.4-0.9-4.6-1.8-6.7l4.2-2.9c-0.7-1.6-1.6-3.1-2.6-4.5 L70,35c-1.4-1.9-3.1-3.5-4.9-4.9l2.2-4.6c-1.4-1-2.9-1.9-4.5-2.6L59.8,27c-2.1-0.9-4.4-1.5-6.7-1.8l-0.4-5.1C51.8,20,50.9,20,50,20 s-1.8,0-2.6,0.1l-0.4,5.1c-2.4,0.3-4.6,0.9-6.7,1.8l-2.9-4.1c-1.6,0.7-3.1,1.6-4.5,2.6l2.1,4.6c-1.9,1.4-3.5,3.1-5,4.9l-4.5-2.1 c-1,1.4-1.9,2.9-2.6,4.5l4.1,2.9c-0.9,2.1-1.5,4.4-1.8,6.8l-5,0.4C20,48.2,20,49.1,20,50s0,1.8,0.1,2.6l5,0.4 c0.3,2.4,0.9,4.7,1.8,6.8l-4.1,2.9c0.7,1.6,1.6,3.1,2.6,4.5l4.5-2.1c1.4,1.9,3.1,3.5,5,4.9l-2.1,4.6c1.4,1,2.9,1.9,4.5,2.6l2.9-4.1 c2.1,0.9,4.4,1.5,6.7,1.8l0.4,5.1C48.2,80,49.1,80,50,80s1.8,0,2.6-0.1l0.4-5.1c2.3-0.3,4.6-0.9,6.7-1.8l2.9,4.2 c1.6-0.7,3.1-1.6,4.5-2.6L65,69.9c1.9-1.4,3.5-3,4.9-4.9l4.6,2.2c1-1.4,1.9-2.9,2.6-4.5L73,59.8c0.9-2.1,1.5-4.4,1.8-6.7L79.9,52.6 z M50,65c-8.3,0-15-6.7-15-15c0-8.3,6.7-15,15-15s15,6.7,15,15C65,58.3,58.3,65,50,65z" fill="currentColor"><animateTransform attributeName="transform" type="rotate" from="90 50 50" to="0 50 50" dur="1s" repeatCount="indefinite"></animateTransform></path></g><g transform="translate(20,20) rotate(15 50 50)"><path d="M79.9,52.6C80,51.8,80,50.9,80,50s0-1.8-0.1-2.6l-5.1-0.4c-0.3-2.4-0.9-4.6-1.8-6.7l4.2-2.9c-0.7-1.6-1.6-3.1-2.6-4.5 L70,35c-1.4-1.9-3.1-3.5-4.9-4.9l2.2-4.6c-1.4-1-2.9-1.9-4.5-2.6L59.8,27c-2.1-0.9-4.4-1.5-6.7-1.8l-0.4-5.1C51.8,20,50.9,20,50,20 s-1.8,0-2.6,0.1l-0.4,5.1c-2.4,0.3-4.6,0.9-6.7,1.8l-2.9-4.1c-1.6,0.7-3.1,1.6-4.5,2.6l2.1,4.6c-1.9,1.4-3.5,3.1-5,4.9l-4.5-2.1 c-1,1.4-1.9,2.9-2.6,4.5l4.1,2.9c-0.9,2.1-1.5,4.4-1.8,6.8l-5,0.4C20,48.2,20,49.1,20,50s0,1.8,0.1,2.6l5,0.4 c0.3,2.4,0.9,4.7,1.8,6.8l-4.1,2.9c0.7,1.6,1.6,3.1,2.6,4.5l4.5-2.1c1.4,1.9,3.1,3.5,5,4.9l-2.1,4.6c1.4,1,2.9,1.9,4.5,2.6l2.9-4.1 c2.1,0.9,4.4,1.5,6.7,1.8l0.4,5.1C48.2,80,49.1,80,50,80s1.8,0,2.6-0.1l0.4-5.1c2.3-0.3,4.6-0.9,6.7-1.8l2.9,4.2 c1.6-0.7,3.1-1.6,4.5-2.6L65,69.9c1.9-1.4,3.5-3,4.9-4.9l4.6,2.2c1-1.4,1.9-2.9,2.6-4.5L73,59.8c0.9-2.1,1.5-4.4,1.8-6.7L79.9,52.6 z M50,65c-8.3,0-15-6.7-15-15c0-8.3,6.7-15,15-15s15,6.7,15,15C65,58.3,58.3,65,50,65z" fill="currentColor"><animateTransform attributeName="transform" type="rotate" from="0 50 50" to="90 50 50" dur="1s" repeatCount="indefinite"></animateTransform></path></g></svg>
</div>
</body>
</html>

View file

@ -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

View file

@ -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/",

View file

@ -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) {}
}
}
})
},
};
});

View file

@ -19,6 +19,14 @@ export const faWindowControlAPI: I_faWindowControlAPI = {
}
},
maximizeWindow () {
const currentWindow = BrowserWindow.getFocusedWindow()
if (currentWindow !== null) {
currentWindow.maximize()
}
},
resizeWindow () {
const currentWindow = BrowserWindow.getFocusedWindow()

View file

@ -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()
})
}

View file

@ -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)
}