Skip to content

Commit

Permalink
feat: tray & app menu item "Check for Updates..."
Browse files Browse the repository at this point in the history
When the check is running, replace "Check for Updates" with a disabled
item "Checking for Updates".

Signed-off-by: Miroslav Bajtoš <saturn@bajtos.net>
  • Loading branch information
bajtos committed Aug 2, 2022
1 parent fca5d68 commit 97373e2
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 56 deletions.
58 changes: 58 additions & 0 deletions main/app-menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const { Menu, MenuItem, ipcMain } = require('electron')
const { ipcMainEvents } = require('./ipc')

function setupAppMenu (/** @type {import('./typings').Context} */ ctx) {
// Add "Check for updates..." item to the Application menu on MacOS
// GitHub issues tracking the work to add this menu item on other platforms:
// - Windows: https://github.com/filecoin-project/filecoin-station/issues/63
// - Linux: https://github.com/filecoin-project/filecoin-station/issues/64
if (process.platform !== 'darwin') return

const menu = Menu.getApplicationMenu()
if (!menu) return

menu.items[0].submenu?.insert(1, new MenuItem({
id: 'checkForUpdates',
label: 'Check For Updates...',
click: () => { ctx.manualCheckForUpdates() }
}))
menu.items[0].submenu?.insert(2, new MenuItem({
id: 'checkingForUpdates',
label: 'Checking For Updates',
enabled: false,
visible: false
}))

Menu.setApplicationMenu(menu)
setupIpcEventListeners(menu)
}

/**
* @param {Electron.Menu} appMenu
*/
function setupIpcEventListeners (appMenu) {
ipcMain.on(ipcMainEvents.UPDATE_CHECK_STARTED, () => {
getItemById('checkForUpdates').visible = false
getItemById('checkingForUpdates').visible = true
})

ipcMain.on(ipcMainEvents.UPDATE_CHECK_FINISHED, () => {
getItemById('checkForUpdates').visible = true
getItemById('checkingForUpdates').visible = false
})

/**
* Get an item from the main app menu or fail with a useful error message.
* @param {string} id
* @returns {Electron.MenuItem}
*/
function getItemById (id) {
const item = appMenu.getMenuItemById(id)
if (!item) throw new Error(`Unknown app menu item id: ${id}`)
return item
}
}

module.exports = {
setupAppMenu
}
52 changes: 52 additions & 0 deletions main/dialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { dialog } = require('electron')
const { IS_MAC } = require('./consts')

module.exports = {
showDialogSync
}

/**
* NOTE: always send the buttons in the order [OK, Cancel, ...Actions].
* See this post for more interesting information about the topic:
* https://medium.muz.li/ok-key-and-cancel-key-which-one-should-be-set-up-on-the-left-4780e86c16eb
*
* @param {import('electron').MessageBoxSyncOptions & {title: string}} args
* @returns
*/
function showDialogSync ({
title,
message,
type = 'info',
buttons = ['OK', 'Cancel'],
...opts
}) {
const options = {
type,
buttons,
title,
message,
...opts
}

if (IS_MAC) {
options.message = title
options.detail = message
}

const isInverse = !IS_MAC

if (isInverse) {
options.buttons.reverse()
}

if (buttons.length > 1) {
options.defaultId = isInverse ? buttons.length - 1 : 0
options.cancelId = isInverse ? buttons.length - 2 : 1
}

const selected = dialog.showMessageBoxSync(options)

return isInverse
? buttons.length - selected - 1
: selected
}
7 changes: 5 additions & 2 deletions main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const setupUI = require('./ui')
const setupTray = require('./tray')
const setupUpdater = require('./updater')
const saturnNode = require('./saturn-node')
const setupIpc = require('./ipc')
const { setupIpcMain } = require('./ipc')
const { setupAppMenu } = require('./app-menu')

const inTest = (process.env.NODE_ENV === 'test')

Expand All @@ -34,6 +35,7 @@ if (!app.requestSingleInstanceLock() && !inTest) {

/** @type {import('./typings').Context} */
const ctx = {
manualCheckForUpdates: () => { throw new Error('never get here') },
showUI: () => { throw new Error('never get here') },
loadWebUIFromDist: serve({ directory: path.resolve(__dirname, '../renderer/dist') })
}
Expand All @@ -49,9 +51,10 @@ async function run () {
try {
// Interface
await setupTray(ctx)
await setupAppMenu(ctx)
await setupUI(ctx)
await setupUpdater(ctx)
await setupIpc()
await setupIpcMain()

await saturnNode.setup(ctx)
} catch (e) {
Expand Down
12 changes: 11 additions & 1 deletion main/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ const { ipcMain } = require('electron')

const saturnNode = require('./saturn-node')

module.exports = function () {
const ipcMainEvents = Object.freeze({
UPDATE_CHECK_STARTED: 'station:update-check:started',
UPDATE_CHECK_FINISHED: 'station:update-check:finished'
})

function setupIpcMain () {
ipcMain.handle('saturn:isRunning', saturnNode.isRunning)
ipcMain.handle('saturn:isReady', saturnNode.isReady)
ipcMain.handle('saturn:start', saturnNode.start)
Expand All @@ -12,3 +17,8 @@ module.exports = function () {
ipcMain.handle('saturn:getFilAddress', saturnNode.getFilAddress)
ipcMain.handle('saturn:setFilAddress', (_event, address) => saturnNode.setFilAddress(address))
}

module.exports = {
setupIpcMain,
ipcMainEvents
}
42 changes: 41 additions & 1 deletion main/tray.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { Menu, Tray, shell, app } = require('electron')
const { Menu, Tray, shell, app, ipcMain } = require('electron')
const path = require('path')
const { IS_MAC, STATION_VERSION } = require('./consts')
const { ipcMainEvents } = require('./ipc')

// Be warned, this one is pretty ridiculous:
// Tray must be global or it will break due to.. GC.
Expand All @@ -23,6 +24,17 @@ module.exports = function (/** @type {import('./typings').Context} */ ctx) {
label: `Filecoin Station v${STATION_VERSION}`,
click: () => { shell.openExternal(`https://github.com/filecoin-project/filecoin-station/releases/v${STATION_VERSION}`) }
},
{
id: 'checkForUpdates',
label: 'Check for Updates...',
click: () => { ctx.manualCheckForUpdates() }
},
{
id: 'checkingForUpdates',
label: 'Checking for Updates',
enabled: false,
visible: false
},
{ type: 'separator' },
{
id: 'showUi',
Expand All @@ -38,4 +50,32 @@ module.exports = function (/** @type {import('./typings').Context} */ ctx) {
])
tray.setToolTip('Filecoin Station')
tray.setContextMenu(contextMenu)

setupIpcEventListeners(contextMenu)
}

/**
* @param {Electron.Menu} contextMenu
*/
function setupIpcEventListeners (contextMenu) {
ipcMain.on(ipcMainEvents.UPDATE_CHECK_STARTED, () => {
getItemById('checkForUpdates').visible = false
getItemById('checkingForUpdates').visible = true
})

ipcMain.on(ipcMainEvents.UPDATE_CHECK_FINISHED, () => {
getItemById('checkForUpdates').visible = true
getItemById('checkingForUpdates').visible = false
})

/**
* Get an item from the Tray menu or fail with a useful error message.
* @param {string} id
* @returns {Electron.MenuItem}
*/
function getItemById (id) {
const item = contextMenu.getMenuItemById(id)
if (!item) throw new Error(`Unknown tray menu item id: ${id}`)
return item
}
}
1 change: 1 addition & 0 deletions main/typings.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface Context {
showUI: () => void
loadWebUIFromDist: import('electron-serve').loadURL
manualCheckForUpdates: () => void
}

0 comments on commit 97373e2

Please sign in to comment.