Skip to content

Commit

Permalink
gui: Fix Tray status updates (#2183)
Browse files Browse the repository at this point in the history
We have witnessed de-synchronizations between the status within the
app's main Window and the systray which we believe arise from close
asynchronous status updates (partially) overwriting each other.
To prevent these, we will now enqueue those updates so only one is
processed at a time.

We also take the opportunity to reduce the number of systray image
changes which can be costly CPU wise.
  • Loading branch information
taratatach committed Dec 15, 2021
2 parents f18aee1 + 621ead0 commit 6c3b891
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 46 deletions.
57 changes: 33 additions & 24 deletions gui/js/tray.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
* @module gui/js/tray
*/

const { Tray, Menu, MenuItem } = require('electron')
const { Tray, Menu, MenuItem, nativeImage } = require('electron')
const { translate } = require('./i18n')
const path = require('path')

let tray = null
let lastIconName = null

const imgs = path.resolve(__dirname, '..', 'images')
const isMac = process.platform === 'darwin'
Expand All @@ -16,15 +17,35 @@ const isKde =
process.env.XDG_CURRENT_DESKTOP &&
process.env.XDG_CURRENT_DESKTOP.match(/KDE/)

const platformIcon = (iconName, { pressed = false } = {}) =>
nativeImage.createFromPath(
isMac
? pressed
? `${imgs}/tray-icon-osx/${iconName}Highlight.png`
: `${imgs}/tray-icon-osx/${iconName}Template.png`
: isWindows
? `${imgs}/tray-icon-win/${iconName}.png`
: isKde
? `${imgs}/tray-icon-linux-kde/${iconName}.png`
: `${imgs}/tray-icon-linux/${iconName}.png`
)

const setImage = iconName => {
const icon = platformIcon(iconName)
tray.setImage(icon)

if (isMac) {
const pressedIcon = platformIcon(iconName, { pressed: true })
tray.setPressedImage(pressedIcon)
}

lastIconName = iconName
}

module.exports.init = (app, listener) => {
let icon = isMac
? `${imgs}/tray-icon-osx/idleTemplate.png`
: isWindows
? `${imgs}/tray-icon-win/idle.png`
: isKde
? `${imgs}/tray-icon-linux-kde/idle.png`
: `${imgs}/tray-icon-linux/idle.png`
tray = new Tray(icon)
tray = new Tray(nativeImage.createEmpty())
setImage('idle')

app.on('before-quit', () => tray.destroy())

let cachedBounds = null
Expand Down Expand Up @@ -125,20 +146,8 @@ const systrayInfo = (status, label) => {
}

const setStatus = (module.exports.setStatus = (status, label) => {
const [icon, tooltip] = systrayInfo(status, label)

const [iconName, tooltip] = systrayInfo(status, label)
tray.setToolTip(tooltip)
if (process.platform === 'darwin') {
tray.setImage(`${imgs}/tray-icon-osx/${icon}Template.png`)
tray.setPressedImage(`${imgs}/tray-icon-osx/${icon}Highlight.png`)
} else if (process.platform === 'win32') {
tray.setImage(`${imgs}/tray-icon-win/${icon}.png`)
} else if (
process.env.XDG_CURRENT_DESKTOP &&
process.env.XDG_CURRENT_DESKTOP.match(/KDE/)
) {
tray.setImage(`${imgs}/tray-icon-linux-kde/${icon}.png`)
} else {
tray.setImage(`${imgs}/tray-icon-linux/${icon}.png`)
}

if (lastIconName !== iconName) setImage(iconName)
})
52 changes: 30 additions & 22 deletions gui/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const pkg = require('../package.json')

const path = require('path')
const os = require('os')
const async = require('async')

const proxy = require('./js/proxy')
const {
Expand Down Expand Up @@ -104,11 +105,11 @@ const setupDesktop = async () => {
desktopIsKO(err)

if (err instanceof config.InvalidConfigError) {
showInvalidConfigError()
await showInvalidConfigError()
} else if (err instanceof migrations.MigrationFailedError) {
showMigrationError(err)
await showMigrationError(err)
} else {
dialog.showMessageBoxSync(null, {
await dialog.showMessageBox(null, {
type: 'error',
message: err.message,
buttons: [translate('AppMenu Close')]
Expand Down Expand Up @@ -167,7 +168,7 @@ const showWindow = async bounds => {
}
}

const showInvalidConfigError = () => {
const showInvalidConfigError = async () => {
const options = {
type: 'warning',
title: translate('InvalidConfiguration Invalid configuration'),
Expand All @@ -180,8 +181,8 @@ const showInvalidConfigError = () => {
buttons: [translate('Button Log out'), translate('Button Contact support')],
defaultId: 0
}
const userChoice = dialog.showMessageBoxSync(null, options)
if (userChoice === 0) {
const { response } = await dialog.showMessageBox(null, options)
if (response === 0) {
desktop
.removeConfig()
.then(() => log.info('removed'))
Expand All @@ -194,7 +195,7 @@ const showInvalidConfigError = () => {
}
}

const showMigrationError = (err /*: Error */) => {
const showMigrationError = async (err /*: Error */) => {
const errorDetails = [`${err.name}:`].concat(
err.errors.map(pouchErr => pouchErr.toString())
)
Expand All @@ -209,14 +210,14 @@ const showMigrationError = (err /*: Error */) => {
buttons: [translate('Button Contact support')],
defaultId: 0
}
const userChoice = dialog.showMessageBoxSync(null, options)
if (userChoice === 0) {
const { response } = await dialog.showMessageBox(null, options)
if (response === 0) {
helpWindow = new HelpWM(app, desktop)
helpWindow.show()
}
}

const sendErrorToMainWindow = ({ msg, code }) => {
const sendErrorToMainWindow = async ({ msg, code }) => {
if (code === COZY_CLIENT_REVOKED_CODE) {
if (notificationsState.revokedAlertShown) return
notificationsState.revokedAlertShown = true // prevent the alert from appearing twice
Expand All @@ -236,8 +237,8 @@ const sendErrorToMainWindow = ({ msg, code }) => {
defaultId: 1
}
trayWindow.hide()
const userChoice = dialog.showMessageBoxSync(null, options)
if (userChoice === 0) {
const { response } = await dialog.showMessageBox(null, options)
if (response === 0) {
desktop
.stopSync()
.then(() => desktop.removeConfig())
Expand All @@ -263,7 +264,7 @@ const sendErrorToMainWindow = ({ msg, code }) => {
defaultId: 0
}
trayWindow.hide()
dialog.showMessageBoxSync(null, options)
await dialog.showMessageBox(null, options)
desktop
.stopSync()
.then(() => desktop.pouch.db.destroy())
Expand All @@ -286,7 +287,7 @@ const sendErrorToMainWindow = ({ msg, code }) => {
detail: translate('SyncDirEmpty Detail'),
buttons: [translate('AppMenu Close')]
}
dialog.showMessageBoxSync(null, options)
await dialog.showMessageBox(null, options)
desktop
.stopSync()
.catch(err => log.error({ err, sentry: true }, 'failed stopping sync'))
Expand All @@ -301,7 +302,7 @@ const sendErrorToMainWindow = ({ msg, code }) => {

const LAST_SYNC_UPDATE_DELAY = 1000 // milliseconds
let lastSyncTimeout = null
const updateState = (newState, data) => {
const updateState = async ({ newState, data }) => {
const { status, filename, userActions, errors } = data || {}

if (newState === 'sync-state') {
Expand Down Expand Up @@ -354,9 +355,9 @@ const updateState = (newState, data) => {
// TODO: get rid of sendErrorToMainWindow and move all error management to
// main window?
if (errors[0].code !== null) {
sendErrorToMainWindow({ code: errors[0].code })
await sendErrorToMainWindow({ code: errors[0].code })
} else {
sendErrorToMainWindow({
await sendErrorToMainWindow({
msg:
errors[0].message ||
translate('Dashboard Synchronization impossible')
Expand All @@ -365,6 +366,13 @@ const updateState = (newState, data) => {
}
}
}
const updateStateQueue = Promise.promisifyAll(async.queue(updateState))

const enqueueStateUpdate = (newState, data) => {
updateStateQueue.pushAsync({ newState, data }).catch(err => {
log.warn({ err }, 'Failed to update state')
})
}

const addFile = async info => {
const file = {
Expand All @@ -374,7 +382,7 @@ const addFile = async info => {
size: info.size || 0,
updated: +new Date()
}
updateState('syncing', file)
enqueueStateUpdate('syncing', file)
await lastFiles.add(file)
await lastFiles.persist()
}
Expand All @@ -387,7 +395,7 @@ const removeFile = async info => {
size: 0,
updated: 0
}
updateState('syncing')
enqueueStateUpdate('syncing')
trayWindow.send('delete-file', file)
await lastFiles.remove(file)
await lastFiles.persist()
Expand All @@ -414,9 +422,9 @@ const sendDiskUsage = () => {
}

const startSync = async () => {
updateState('syncing')
enqueueStateUpdate('syncing')
desktop.events.on('sync-state', state => {
updateState('sync-state', state)
enqueueStateUpdate('sync-state', state)
})
desktop.events.on('transfer-started', addFile)
desktop.events.on('transfer-copy', addFile)
Expand Down Expand Up @@ -538,7 +546,7 @@ app.on('ready', async () => {
desktop = new Desktop.App(process.env.COZY_DESKTOP_DIR)
} catch (err) {
if (err.message.match(/GLIBCXX/)) {
dialog.showMessageBoxSync(null, {
await dialog.showMessageBox(null, {
type: 'error',
message: translate('Error Bad GLIBCXX version'),
buttons: [translate('AppMenu Close')]
Expand Down

0 comments on commit 6c3b891

Please sign in to comment.