Skip to content

Commit

Permalink
feat(ux): move preferences to menubar
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 22, 2020
1 parent 7188aac commit f66bd70
Show file tree
Hide file tree
Showing 16 changed files with 340 additions and 199 deletions.
54 changes: 53 additions & 1 deletion assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"readReleaseNotes": "Read Release Notes",
"status": "Status",
"files": "Files",
"settings": "Settings",
"peers": "Peers",
"quit": "Quit",
"versions": "Versions",
"screenshotTaken": "Screenshot taken",
Expand All @@ -32,6 +32,7 @@
"close": "Close",
"ok": "OK",
"cancel": "Cancel",
"enable": "Enable",
"reportTheError": "Report the error",
"restartIpfsDesktop": "Restart IPFS Desktop",
"openLogs": "Open logs",
Expand Down Expand Up @@ -171,5 +172,56 @@
"couldNotSaveDialog": {
"title": "Could not write to disk",
"message": "There was an error writing to the disk. Please try again."
},
"launchAtLoginNotSupported": {
"title": "Error",
"message": "Launch at login is not supported on your platform."
},
"launchAtLoginFailed": {
"title": "Error",
"message": "Launch at login could not be enabled on your machine."
},
"enableIpfsOnPath": {
"title": "Enable IPFS on PATH",
"message": "By enabling this option, IPFS will be available on your command line as \"ipfs\". This action is reversible.",
"action": "Enable"
},
"disableIpfsOnPath": {
"title": "Disable IPFS on PATH",
"message": "By disabling this option, IPFS will no longer be available on your command line as \"ipfs\".",
"action": "Disable"
},
"enableGlobalTakeScreenshotShortcut": {
"title": "Enable screenshot shortcut",
"message": "By enabling this, the shortcut \"{ accelerator }\" will be available to take screenshots as long as IPFS Desktop is running."
},
"enableGlobalDownloadShortcut": {
"title": "Enable download shortcut",
"message": "By enabling this, the shortcut \"{ accelerator }\" will be available to download files as long as IPFS Desktop is running."
},
"installNpmOnIpfsWarning": {
"title": "Install npm on IPFS",
"message": "This experimental feature installs the \"ipfs-npm\" package on your system. It requires Node.js to be installed.",
"action": "Install"
},
"unableToInstallNpmOnIpfs": {
"title": "Error",
"message": "It was not possible to install \"ipfs-npm\" package on your system. Please check the logs for more information or try installing it manually by running \"npm install -g ipfs-npm\" on your command line."
},
"unableToUninstallNpmOnIpfs": {
"title": "Error",
"message": "It was not possible to uninstall \"ipfs-npm\" package on your system. Please check the logs for more information or try uninstalling it manually by running \"npm uninstall -g ipfs-npm\" on your command line."
},
"settings": {
"settings": "Settings",
"preferences": "Preferences",
"openNodeSettings": "Open Node Settings",
"desktopIntegrations": "Desktop Integrations",
"launchOnStartup": "Launch at Login",
"ipfsCommandLineTools": "Command Line Tools",
"takeScreenshotShortcut": "Global Screenshot Shortcut",
"downloadHashShortcut": "Global Download Shortcut",
"experiments": "Experiments",
"npmOnIpfs": "npm on IPFS"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"clean:webui": "shx rm -rf assets/webui/",
"build": "run-s clean:webui build:*",
"build:webui": "run-s build:webui:*",
"build:webui:download": "npx ipfs-or-gateway -c bafybeidatpz2hli6fgu3zul5woi27ujesdf5o5a7bu622qj6ugharciwjq -p assets/webui/ -t 360000 --verbose",
"build:webui:download": "npx ipfs-or-gateway -c bafybeicevz7mmgt45zitnmg73khkmvioz2ozixvd6guujphhtsenu2pt2a -p assets/webui/ -t 360000 --verbose",
"build:webui:minimize": "shx rm -rf assets/webui/static/js/*.map && shx rm -rf assets/webui/static/css/*.map",
"build:binaries": "electron-builder --publish onTag"
},
Expand Down
43 changes: 37 additions & 6 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 @@ -47,22 +49,40 @@ async function disable () {
await fs.remove(getDesktopFile())
}

module.exports = async function (ctx) {
const activate = async (value, oldValue) => {
module.exports = async function () {
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,10 +93,21 @@ module.exports = async function (ctx) {
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))
createToggler(ctx, CONFIG_KEY, activate)
activate({ newValue: store.get(CONFIG_KEY, false) })
createToggler(CONFIG_KEY, activate)
}

module.exports.CONFIG_KEY = CONFIG_KEY
module.exports.isSupported = isSupported
23 changes: 7 additions & 16 deletions src/create-toggler.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
const { ipcMain } = require('electron')
const os = require('os')
const store = require('./common/store')
const logger = require('./common/logger')

module.exports = function ({ webui }, settingsOption, activate) {
ipcMain.on('config.toggle', async (_, opt) => {
if (opt !== settingsOption) {
return
}

module.exports = function (settingsOption, activate) {
ipcMain.on(`toggle_${settingsOption}`, async () => {
const oldValue = store.get(settingsOption, null)
const newValue = !oldValue
let success = false

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

const action = newValue ? 'enabled' : 'disabled'
logger.info(`[${settingsOption}] ${action}`)
}

webui.webContents.send('config.changed', {
config: store.store,
changed: settingsOption,
platform: os.platform(),
success
})
// We always emit the event so any handlers for it can act upon
// the current configuration, whether it was successfully
// updated or not.
ipcMain.emit('configUpdated')
})
}
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
7 changes: 6 additions & 1 deletion src/download-cid.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ async function downloadCid (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 All @@ -142,3 +146,4 @@ module.exports = function (ctx) {

module.exports.downloadCid = downloadCid
module.exports.SHORTCUT = SHORTCUT
module.exports.CONFIG_KEY = CONFIG_KEY
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))
})
})
}
55 changes: 45 additions & 10 deletions src/ipfs-on-path/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,57 @@ 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(ctx, 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('enableIpfsOnPath.title'),
message: i18n.t('enableIpfsOnPath.message'),
buttons: [
i18n.t('enableIpfsOnPath.action'),
i18n.t('cancel')
]
}) !== 0) {
// User canceled
return
}

return run('install')
}

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

return run('uninstall')
})

firstTime()
}

module.exports.CONFIG_KEY = CONFIG_KEY

async function firstTime () {
// Check if we've done this before.
if (store.get(CONFIG_KEY, null) !== null) {
Expand Down Expand Up @@ -54,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 @@ -78,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
})
}
Loading

0 comments on commit f66bd70

Please sign in to comment.