Skip to content

Commit

Permalink
feat: add install/enable/disable/error messages
Browse files Browse the repository at this point in the history
License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
  • Loading branch information
hacdias committed Apr 18, 2020
1 parent 6e9fb92 commit 816117d
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 38 deletions.
27 changes: 27 additions & 0 deletions assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"close": "Close",
"ok": "OK",
"cancel": "Cancel",
"enable": "Enable",
"reportTheError": "Report the error",
"restartIpfsDesktop": "Restart IPFS Desktop",
"openLogs": "Open logs",
Expand Down Expand Up @@ -150,6 +151,32 @@
"title": "IPFS on PATH",
"message": "Could not add IPFS to the PATH."
},
"launchAtLoginNotSupported": {
"title": "Launch at login",
"message": "Launch at login is not supported on your platform."
},
"launchAtLoginFailed": {
"title": "Launch at login",
"message": "Launch at login could not be enabled on your machine."
},
"ipfsOnPathInstall": {
"title": "Install IPFS on PATH",
"message": "By enabling this option, IPFS will be available on your command line as \"ipfs\". This action is reversible.",
"action": "Install"
},
"ipfsOnPathUninstall": {
"title": "Uninstall IPFS on PATH",
"message": "By disabling this option, IPFS will no longer be available on your command line as \"ipfs\".",
"action": "Uninstall"
},
"enableGlobalTakeScreenshotShortcut": {
"title": "Take screenshot shortcut",
"message": "By enabling this, the shortcut { accelerator } will be available to take screenshots as long as IPFS Desktop is running."
},
"enableGlobalDownloadShortcut": {
"title": "Download shortcut",
"message": "By enabling this, the shortcut { accelerator } will be available to download files as long as IPFS Desktop is running."
},
"settings": {
"settings": "Settings",
"preferences": "Preferences",
Expand Down
37 changes: 33 additions & 4 deletions src/auto-launch.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { app } = require('electron')
const i18n = require('i18next')
const os = require('os')
const path = require('path')
const fs = require('fs-extra')
Expand All @@ -7,6 +8,7 @@ const createToggler = require('./create-toggler')
const logger = require('./common/logger')
const store = require('./common/store')
const { IS_MAC, IS_WIN } = require('./common/consts')
const { showDialog, recoverableErrorDialog } = require('./dialogs')

const CONFIG_KEY = 'autoLaunch'

Expand Down Expand Up @@ -48,21 +50,39 @@ async function disable () {
}

module.exports = async function () {
const activate = async (value, oldValue) => {
const activate = async ({ newValue, oldValue, feedback }) => {
if (process.env.NODE_ENV === 'development') {
logger.info('[launch on startup] unavailable during development')

if (feedback) {
showDialog({
title: 'Launch at Login',
message: 'Not available during development.',
buttons: [i18n.t('close')]
})
}

return
}

if (!isSupported()) {
logger.info('[launch on startup] not supported on this platform')

if (feedback) {
showDialog({
title: i18n.t('launchAtLoginNotSupported.title'),
message: i18n.t('launchAtLoginNotSupported.message'),
buttons: [i18n.t('close')]
})
}

return false
}

if (value === oldValue) return
if (newValue === oldValue) return

try {
if (value === true) {
if (newValue === true) {
await enable()
logger.info('[launch on startup] enabled')
} else {
Expand All @@ -73,12 +93,21 @@ module.exports = async function () {
return true
} catch (err) {
logger.error(`[launch on startup] ${err.toString()}`)

if (feedback) {
recoverableErrorDialog(err, {
title: i18n.t('launchAtLoginFailed.title'),
message: i18n.t('launchAtLoginFailed.message')
})
}

return false
}
}

activate(store.get(CONFIG_KEY, false))
activate({ newValue: store.get(CONFIG_KEY, false) })
createToggler(CONFIG_KEY, activate)
}

module.exports.CONFIG_KEY = CONFIG_KEY
module.exports.isSupported = isSupported
2 changes: 1 addition & 1 deletion src/create-toggler.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module.exports = function (settingsOption, activate) {
// TODO: refactor: tell the user if didn't work or not available.
// Receive prompt() to ask user if they're sure they want to enable for some.

if (await activate(newValue, oldValue)) {
if (await activate({ newValue, oldValue, feedback: true })) {
store.set(settingsOption, newValue)

const action = newValue ? 'enabled' : 'disabled'
Expand Down
14 changes: 8 additions & 6 deletions src/dialogs/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function criticalErrorDialog (e) {
// Shows a recoverable error dialog with the default title and message.
// Passing an options object alongside the error can be used to override
// the title and message.
function recoverableErrorDialog (e, options = {}) {
function recoverableErrorDialog (e, options) {
const cfg = {
title: i18n.t('recoverableErrorDialog.title'),
message: i18n.t('recoverableErrorDialog.message'),
Expand All @@ -59,12 +59,14 @@ function recoverableErrorDialog (e, options = {}) {
]
}

if (options.title) {
cfg.title = options.title
}
if (options) {
if (options.title) {
cfg.title = options.title
}

if (options.message) {
cfg.message = options.message
if (options.message) {
cfg.message = options.message
}
}

const option = dialog(cfg)
Expand Down
6 changes: 5 additions & 1 deletion src/download-hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ async function downloadHash (ctx) {
}

module.exports = function (ctx) {
setupGlobalShortcut(ctx, {
setupGlobalShortcut({
confirmationDialog: {
title: i18n.t('enableGlobalDownloadShortcut.title'),
message: i18n.t('enableGlobalDownloadShortcut.message', { accelerator: SHORTCUT })
},
settingsOption: CONFIG_KEY,
accelerator: SHORTCUT,
action: () => {
Expand Down
10 changes: 5 additions & 5 deletions src/exec-or-sudo.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const env = {
sudo: 'env ELECTRON_RUN_AS_NODE=1'
}

const getResult = (err, stdout, stderr, scope, failSilently) => {
const getResult = (err, stdout, stderr, scope, failSilently, errorOptions) => {
if (stdout) {
logger.info(`[${scope}] sudo: stdout: ${stdout.toString().trim()}`)
}
Expand All @@ -37,14 +37,14 @@ const getResult = (err, stdout, stderr, scope, failSilently) => {
} else if (str.includes('User did not grant permission')) {
dialog.showErrorBox(i18n.t('noPermissionDialog.title'), i18n.t('noPermissionDialog.message'))
} else {
recoverableErrorDialog(err)
recoverableErrorDialog(err, errorOptions)
}
}

return false
}

module.exports = async function ({ script, scope, failSilently, trySudo = true }) {
module.exports = async function ({ script, scope, failSilently, trySudo = true, errorOptions }) {
const dataArg = `--data="${app.getPath('userData')}"`
let err = null

Expand All @@ -61,7 +61,7 @@ module.exports = async function ({ script, scope, failSilently, trySudo = true }

if (!trySudo) {
if (!failSilently) {
recoverableErrorDialog(err)
recoverableErrorDialog(err, errorOptions)
}

return false
Expand All @@ -71,7 +71,7 @@ module.exports = async function ({ script, scope, failSilently, trySudo = true }
const command = `${env.sudo} "${process.execPath}" "${script}" ${dataArg}`
return new Promise(resolve => {
sudo.exec(command, { name: 'IPFS Desktop' }, (err, stdout, stderr) => {
resolve(getResult(err, stdout, stderr, scope, failSilently))
resolve(getResult(err, stdout, stderr, scope, failSilently, errorOptions))
})
})
}
53 changes: 43 additions & 10 deletions src/ipfs-on-path/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,49 @@ const execOrSudo = require('../exec-or-sudo')
const logger = require('../common/logger')
const store = require('../common/store')
const { IS_WIN } = require('../common/consts')
const { recoverableErrorDialog } = require('../dialogs')
const { showDialog, recoverableErrorDialog } = require('../dialogs')

const CONFIG_KEY = 'ipfsOnPath'

module.exports = async function (ctx) {
createToggler(CONFIG_KEY, async (value, oldValue) => {
if (value === oldValue || (oldValue === null && !value)) return
if (value === true) return run('install')
const errorMessage = {
title: i18n.t('cantAddIpfsToPath.title'),
message: i18n.t('cantAddIpfsToPath.message')
}

module.exports = async function () {
createToggler(CONFIG_KEY, async ({ newValue, oldValue }) => {
if (newValue === oldValue || (oldValue === null && !newValue)) {
return
}

if (newValue === true) {
if (showDialog({
title: i18n.t('ipfsOnPathInstall.title'),
message: i18n.t('ipfsOnPathInstall.message'),
buttons: [
i18n.t('ipfsOnPathInstall.action'),
i18n.t('cancel')
]
}) !== 0) {
// User canceled
return
}

return run('install')
}

if (showDialog({
title: i18n.t('ipfsOnPathUninstall.title'),
message: i18n.t('ipfsOnPathUninstall.message'),
buttons: [
i18n.t('ipfsOnPathUninstall.action'),
i18n.t('cancel')
]
}) !== 0) {
// User canceled
return
}

return run('uninstall')
})

Expand Down Expand Up @@ -56,10 +91,7 @@ async function runWindows (script, { failSilently }) {
logger.error(`[ipfs on path] ${err.toString()}`)

if (!failSilently) {
recoverableErrorDialog(err, {
title: i18n.t('cantAddIpfsToPath.title'),
message: i18n.t('cantAddIpfsToPath.message')
})
recoverableErrorDialog(err, errorMessage)
}

return resolve(false)
Expand All @@ -80,6 +112,7 @@ async function run (script, { trySudo = true, failSilently = false } = {}) {
script: join(__dirname, `./scripts/${script}.js`),
scope: 'ipfs on path',
trySudo,
failSilently
failSilently,
errorOptions: errorMessage
})
}
8 changes: 4 additions & 4 deletions src/npm-on-ipfs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ const CONFIG_KEY = 'experiments.npmOnIpfs'
module.exports = function (ctx) {
let interval = null

createToggler(CONFIG_KEY, async (value, oldValue) => {
if (value === oldValue || oldValue === null) return true
createToggler(CONFIG_KEY, async ({ newValue, oldValue }) => {
if (newValue === oldValue || oldValue === null) return true

// If the user is telling to (un)install even though they have (un)installed
// ipfs-npm package manually.
const manual = isPkgInstalled() === value
const manual = isPkgInstalled() === newValue

if (value === true) {
if (newValue === true) {
if (!manual && !await pkg.install()) return false
interval = setInterval(existsAndUpdate, 43200000) // every 12 hours
return true
Expand Down
25 changes: 20 additions & 5 deletions src/setup-global-shortcut.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
const { globalShortcut, ipcMain } = require('electron')
const i18n = require('i18next')
const createToggler = require('./create-toggler')
const store = require('./common/store')
const { IS_MAC } = require('./common/consts')
const { showDialog } = require('./dialogs')

// This function registers a global shortcut/accelerator with a certain action
// and (de)activates it according to its 'settingsOption' value on settings.
module.exports = function (ctx, { settingsOption, accelerator, action }) {
const activate = (value, oldValue) => {
if (value === oldValue) return
module.exports = function ({ settingsOption, accelerator, action, confirmationDialog }) {
const activate = ({ newValue, oldValue }) => {
if (newValue === oldValue) return

if (newValue === true) {
if (confirmationDialog) {
if (showDialog({
...confirmationDialog,
buttons: [
i18n.t('enable'),
i18n.t('cancel')
]
}) !== 0) {
// User canceled
return
}
}

if (value === true) {
globalShortcut.register(accelerator, action)
} else {
globalShortcut.unregister(accelerator)
Expand All @@ -18,7 +33,7 @@ module.exports = function (ctx, { settingsOption, accelerator, action }) {
return true
}

activate(store.get(settingsOption, false))
activate({ newValue: store.get(settingsOption, false) })
createToggler(settingsOption, activate)

if (!IS_MAC) {
Expand Down
6 changes: 5 additions & 1 deletion src/take-screenshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@ function takeScreenshot (ctx) {
}

module.exports = function (ctx) {
setupGlobalShortcut(ctx, {
setupGlobalShortcut({
confirmationDialog: {
title: i18n.t('enableGlobalTakeScreenshotShortcut.title'),
message: i18n.t('enableGlobalTakeScreenshotShortcut.message', { accelerator: SHORTCUT })
},
settingsOption: CONFIG_KEY,
accelerator: SHORTCUT,
action: () => {
Expand Down
4 changes: 3 additions & 1 deletion src/tray.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const runGarbageCollector = require('./run-gc')

const { SHORTCUT: SCREENSHOT_SHORTCUT, CONFIG_KEY: SCREENSHOT_KEY, takeScreenshot } = require('./take-screenshot')
const { SHORTCUT: HASH_SHORTCUT, CONFIG_KEY: HASH_KEY, downloadHash } = require('./download-hash')
const { CONFIG_KEY: AUTO_LAUNCH_KEY } = require('./auto-launch')
const { CONFIG_KEY: AUTO_LAUNCH_KEY, isSupported: supportsLaunchAtLogin } = require('./auto-launch')
const { CONFIG_KEY: IPFS_PATH_KEY } = require('./ipfs-on-path')
const { CONFIG_KEY: NPM_IPFS_KEY } = require('./npm-on-ipfs')

Expand Down Expand Up @@ -232,6 +232,8 @@ module.exports = function (ctx) {
const updateStatus = data => {
status = data

menu.getMenuItemById(AUTO_LAUNCH_KEY).enabled = supportsLaunchAtLogin()

menu.getMenuItemById('ipfsIsStarting').visible = status === STATUS.STARTING_STARTED
menu.getMenuItemById('ipfsIsRunning').visible = status === STATUS.STARTING_FINISHED
menu.getMenuItemById('ipfsIsStopping').visible = status === STATUS.STOPPING_STARTED
Expand Down

0 comments on commit 816117d

Please sign in to comment.