Skip to content

Commit

Permalink
feat: add splash screen on top of ctx refactor (#2548)
Browse files Browse the repository at this point in the history
* chore: update ctx refactor

* fix: tests

* feat: add context.spec.js tests

* fix: startup issues

* feat: app context supports lazy functions

* chore: self-PR-review cleanup

* test: fix e2e tests

* feat: add splash screen

* tmp: trying to fix e2e tests

* chore(deps): upgrade playwright

* fix: revert daemonReady function

* Update src/splash/splash.html

* Update src/splash/splash.html

* Update src/splash/splash.html

* Update src/webui/index.js

* Update test/e2e/launch.e2e.test.js

* Update test/e2e/launch.e2e.test.js

* Update test/e2e/launch.e2e.test.js

* Update test/e2e/launch.e2e.test.js

* Update test/e2e/launch.e2e.test.js

* fix: do not prompt for new port when using CI

* fix: handle errors that were breaking e2e tests

* feat: splashscreen page background is transparent

* make keyframe animation dir normal
* remove visual artifacts of drop-shadow on size reduction
* make drop-shadow animation better

* feat: splash screen animation polish

* chore: removed webkit keyframes definition

* chore: remove debugging code

* chore: remove extra line

Co-authored-by: Nishant Arora <[email protected]>

* chore(splash-screen): repositioning awaits

Co-authored-by: Nishant Arora <[email protected]>

* chore: fix after pr comment change

* chore: move splash screen css to separate file

---------

Co-authored-by: Nishant Arora <[email protected]>
  • Loading branch information
SgtPooki and whizzzkid authored Aug 7, 2023
1 parent 2b22786 commit 417875d
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 12 deletions.
67 changes: 67 additions & 0 deletions assets/pages/splash.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
body {
border-radius: 5vh;
}

.loader {
background-image: url('../../assets/icons/tray/others/on-large.png');
width: 200px;
height: 200px;
-webkit-animation: heartbeat 1.5s ease-in-out 0s infinite normal both;
animation: heartbeat 1.5s ease-in-out 0s infinite normal both;
-webkit-transform-origin: center center;
transform-origin: center center;
margin: auto;
left: 0;
right: 0;
top: 0px;
bottom: 0;
position: fixed;
background-size: cover;
filter: drop-shadow(0 0 0 #378085);
}

@keyframes heartbeat {
0% {
animation-timing-function: ease-out;
transform: scale(1);
transform-origin: center center;
filter: drop-shadow(0 0 0 #378085);
}

45% {
animation-timing-function: ease-in;
transform: scale(1);
filter: drop-shadow(0 0 0rem #378085);
}

50% {
animation-timing-function: ease-in;
transform: scale(1);
filter: drop-shadow(0 0 0.5rem #378085);
}

55% {
animation-timing-function: ease-out;
transform: scale(1);
/* Size should be increased on drop-shadow so when scaling down, drop-shadow compensates for size reduction, and we don't get any visual artifacts */
filter: drop-shadow(0 0 1rem #378085);
}

67% {
animation-timing-function: ease-in;
transform: scale(1.13);
filter: drop-shadow(0 0 0.75rem #378085);
}

83% {
animation-timing-function: ease-out;
transform: scale(1.02);
filter: drop-shadow(0 0 0.50rem #378085);
}

90% {
animation-timing-function: ease-in;
transform: scale(1.09);
filter: drop-shadow(0 0 0.75rem #378085);
}
}
12 changes: 12 additions & 0 deletions assets/pages/splash.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>

<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="splash.css">
</head>

<body id="e2e-splashScreen">
<div class="loader"></div>
</body>

</html>
10 changes: 8 additions & 2 deletions src/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const pDefer = require('p-defer')
const logger = require('./common/logger')

/**
* @typedef {'tray-menu' | 'tray' | 'tray-menu-state' | 'tray.update-menu' | 'countlyDeviceId' | 'manualCheckForUpdates' | 'startIpfs' | 'stopIpfs' | 'restartIpfs' | 'getIpfsd' | 'launchWebUI' | 'webui'} ContextProperties
* @typedef {'tray-menu' | 'tray' | 'tray-menu-state' | 'tray.update-menu' | 'countlyDeviceId' | 'manualCheckForUpdates' | 'startIpfs' | 'stopIpfs' | 'restartIpfs' | 'getIpfsd' | 'launchWebUI' | 'webui' | 'splashScreen'} ContextProperties
*/

/**
Expand Down Expand Up @@ -101,7 +101,13 @@ class Context {

return async (...args) => {
const originalFn = await originalFnPromise
return originalFn(...args)
try {
return await originalFn(...args)
} catch (err) {
logger.error(`[ctx] Error calling ${String(propertyName)}`)
logger.error(err)
throw err
}
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/daemon/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,12 @@ async function checkPorts (ipfsd) {
}

// two "0" in config mean "pick free ports without any prompt"
const promptUser = (apiPort !== 0 || gatewayPort !== 0)
let promptUser = (apiPort !== 0 || gatewayPort !== 0)

if (process.env.NODE_ENV === 'test' || process.env.CI != null) {
logger.info('[daemon] CI or TEST mode, skipping busyPortDialog')
promptUser = false
}

if (promptUser) {
let useAlternativePorts = null
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const setupAnalytics = require('./analytics')
const setupSecondInstance = require('./second-instance')
const { analyticsKeys } = require('./analytics/keys')
const handleError = require('./handleError')
const createSplashScreen = require('./splash/create-splash-screen')

// Hide Dock
if (app.dock) app.dock.hide()
Expand Down Expand Up @@ -65,6 +66,7 @@ async function run () {

try {
await Promise.all([
createSplashScreen(),
setupDaemon(), // ctx.getIpfsd, startIpfs, stopIpfs, restartIpfs
setupAnalytics(), // ctx.countlyDeviceId
setupI18n(),
Expand Down
35 changes: 35 additions & 0 deletions src/splash/create-splash-screen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* A splash screen for the application that is shown while the webui is loading.
*
* This is to prevent the user from seeing the `Could not connect to the IPFS API` error
* while we're still booting up the daemon.
*/
const { BrowserWindow } = require('electron')
const getCtx = require('../context')
const logger = require('../common/logger')
const path = require('node:path')

module.exports = async function createSplashScreen () {
const ctx = getCtx()
const splashScreen = new BrowserWindow({
title: 'IPFS Desktop splash screen',
width: 250,
height: 275,
transparent: true,
frame: false,
alwaysOnTop: true,
show: false
})

try {
await splashScreen.loadFile(path.join(__dirname, '../../assets/pages/splash.html'))
} catch (err) {
logger.error('[splashScreen] loadFile failed')
logger.error(err)
return
}

splashScreen.center()

ctx.setProp('splashScreen', splashScreen)
}
1 change: 1 addition & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type CONFIG_KEYS = 'AUTO_LAUNCH' | 'AUTO_GARBAGE_COLLECTOR' | 'SCREENSHOT_SHORTCUT' | 'OPEN_WEBUI_LAUNCH' | 'MONOCHROME_TRAY_ICON' | 'EXPERIMENT_PUBSUB' | 'EXPERIMENT_PUBSUB_NAMESYS'
59 changes: 50 additions & 9 deletions src/webui/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ const Countly = require('countly-sdk-nodejs')
const { analyticsKeys } = require('../analytics/keys')
const ipcMainEvents = require('../common/ipc-main-events')
const getCtx = require('../context')
const { STATUS } = require('../daemon/consts')

serve({ scheme: 'webui', directory: join(__dirname, '../../assets/webui') })

/**
*
* @returns {BrowserWindow}
*/
const createWindow = () => {
logger.info('[webui] creating window')
const dimensions = screen.getPrimaryDisplay()
Expand Down Expand Up @@ -107,6 +112,7 @@ const createWindow = () => {
})

app.on('before-quit', () => {
logger.info('[web ui] app-quit requested')
// Makes sure the app quits even though we prevent
// the closing of this window.
window.removeAllListeners('close')
Expand Down Expand Up @@ -143,6 +149,10 @@ module.exports = async function () {
url.searchParams.set('deviceId', await ctx.getProp('countlyDeviceId'))

ctx.setProp('launchWebUI', async (path, { focus = true, forceRefresh = false } = {}) => {
if (window.isDestroyed()) {
logger.error(`[web ui] window is destroyed, not launching web ui with ${path}`)
return
}
if (forceRefresh) window.webContents.reload()
if (!path) {
logger.info('[web ui] launching web ui', { withAnalytics: analyticsKeys.FN_LAUNCH_WEB_UI })
Expand All @@ -165,8 +175,10 @@ module.exports = async function () {
}

const getIpfsd = ctx.getFn('getIpfsd')
ipcMain.on(ipcMainEvents.IPFSD, async () => {
let ipfsdStatus = null
ipcMain.on(ipcMainEvents.IPFSD, async (status) => {
const ipfsd = await getIpfsd(true)
ipfsdStatus = status

if (ipfsd && ipfsd.apiAddr !== apiAddress) {
apiAddress = ipfsd.apiAddr
Expand All @@ -183,18 +195,47 @@ module.exports = async function () {
})

const launchWebUI = ctx.getFn('launchWebUI')
const splashScreen = await ctx.getProp('splashScreen')
if (store.get(CONFIG_KEY)) {
// we're supposed to show the window on startup, display the splash screen
splashScreen.show()
} else {
// we don't need the splash screen, ignore it.
splashScreen.destroy()
}
let splashScreenTimeoutId = null
window.on('close', () => {
if (splashScreenTimeoutId) {
clearTimeout(splashScreenTimeoutId)
splashScreenTimeoutId = null
}
})
const handleSplashScreen = async () => {
if ([null, STATUS.STARTING_STARTED].includes(ipfsdStatus)) {
splashScreenTimeoutId = setTimeout(handleSplashScreen, 500)
return
}

await launchWebUI('/')
try {
splashScreen.destroy()
} catch (err) {
logger.error('[web ui] failed to hide splash screen')
logger.error(err)
}
}

return /** @type {Promise<void>} */(new Promise(resolve => {
window.once('ready-to-show', async () => {
logger.info('[web ui] window ready')
// the electron window is ready, but the webui may not have successfully attempted to connect to the daemon yet.
if (store.get(CONFIG_KEY)) {
logger.info('[web ui] waiting for ipfsd to start')
window.once('ready-to-show', async () => {
logger.info('[web ui] window ready')

if (store.get(CONFIG_KEY)) {
await launchWebUI('/')
}
handleSplashScreen()

resolve()
})
resolve()
})
}

updateLanguage()
window.loadURL(url.toString())
Expand Down
11 changes: 11 additions & 0 deletions test/e2e/launch.e2e.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ test.describe.serial('Application launch', async () => {
}
})

/**
*
* @param {Object} [param0]
* @param {string} param0.repoPath
* @returns {Promise<{ app: Awaited<ReturnType<import('playwright')._electron['launch']>>, repoPath: string, home: string }>
*/
async function startApp ({ repoPath } = {}) {
const home = tmp.dirSync({ prefix: 'tmp_home_', unsafeCleanup: true }).name
if (!repoPath) {
Expand All @@ -43,6 +49,11 @@ test.describe.serial('Application launch', async () => {
return { app, repoPath, home }
}

/**
*
* @param {Awaited<ReturnType<import('playwright')._electron['launch']>>} app
* @returns {Promise<{ peerId: string }>
*/
async function daemonReady (app) {
const peerId = await app.evaluate(async ({ ipcMain }) => new Promise((resolve, reject) => {
ipcMain.on('ipfsd', (status, peerId) => {
Expand Down

0 comments on commit 417875d

Please sign in to comment.