Skip to content

Commit

Permalink
feat(ux): improve download cid (#1419)
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 authored Apr 22, 2020
1 parent 22fa2d7 commit 7188aac
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 116 deletions.
30 changes: 23 additions & 7 deletions assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@
"errorwhileTakingScreenshot": "An error occurred while taking the screenshot.",
"clickToOpenLogs": "Click here to open the logs.",
"ipfsNotRunning": "IPFS is not running",
"cantDownloadHash": "Could not download hash",
"invalidHashClipboard": "The hash on the clipboard is not valid.",
"errorWhileDownloadingHash": "An error occurred while getting the hash.",
"errorWhileWritingFiles": "An error occurred while writing the files to your file system.",
"hashDownloaded": "Hash downloaded",
"hashDownloadedClickToView": "Hash { hash } content downloaded. Click to view.",
"checkForUpdates": "Check for Updates...",
"yes": "Yes",
"no": "No",
Expand All @@ -42,7 +36,7 @@
"restartIpfsDesktop": "Restart IPFS Desktop",
"openLogs": "Open logs",
"takeScreenshot": "Take Screenshot",
"downloadHash": "Download Hash",
"downloadCid": "Download...",
"moveRepositoryLocation": "Move Repository Location",
"runGarbageCollector": "Run Garbage Collector",
"selectDirectory": "Select Directory",
Expand Down Expand Up @@ -155,5 +149,27 @@
"runGarbageCollectorErrored": {
"title": "Garbage collector",
"message": "The garbage collector run could not be completed successfully."
},
"downloadCidContentDialog": {
"title": "Download to a local directory",
"message": "Enter a CID, IPFS path, or IPNS path:",
"action": "Next"
},
"cantResolveCidDialog": {
"title": "Error",
"message": "Unable to resolve \"{ path }\"."
},
"couldNotGetCidDialog": {
"title": "Error",
"message": "Unable to fetch \"{ path }\"."
},
"contentsSavedDialog": {
"title": "Success",
"message": "The contents of \"{ path }\" were successfully downloaded.",
"action": "See Files"
},
"couldNotSaveDialog": {
"title": "Could not write to disk",
"message": "There was an error writing to the disk. Please try again."
}
}
144 changes: 144 additions & 0 deletions src/download-cid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
const { join } = require('path')
const fs = require('fs-extra')
const i18n = require('i18next')
const isIPFS = require('is-ipfs')
const { clipboard, app, shell } = require('electron')
const logger = require('./common/logger')
const { IS_MAC } = require('./common/consts')
const setupGlobalShortcut = require('./setup-global-shortcut')
const { selectDirectory } = require('./dialogs')
const dock = require('./dock')
const showPrompt = require('./prompt')
const { showDialog } = require('./dialogs')

const CONFIG_KEY = 'downloadHashShortcut'

const SHORTCUT = IS_MAC
? 'Command+Control+H'
: 'CommandOrControl+Alt+D'

async function saveFile (dir, file) {
const location = join(dir, file.path)
await fs.outputFile(location, file.content)
}

async function getCID () {
const text = clipboard.readText().trim()

const { button, input } = await showPrompt({
title: i18n.t('downloadCidContentDialog.title'),
message: i18n.t('downloadCidContentDialog.message'),
defaultValue: (isIPFS.cid(text) || isIPFS.path(text)) ? text : '',
buttons: [
i18n.t('downloadCidContentDialog.action'),
i18n.t('cancel')
],
window: {
width: 500,
height: 120
}
})

if (button !== 0) {
return
}

return input
}

async function downloadCid (ctx) {
const cid = await getCID()
if (!cid) {
logger.info('[cid download] user canceled')
return
}

const { getIpfsd } = ctx
const ipfsd = await getIpfsd()

if (!ipfsd) {
return
}

let path
try {
path = await ipfsd.api.resolve(cid)
} catch (_) {
showDialog({
title: i18n.t('cantResolveCidDialog.title'),
message: i18n.t('cantResolveCidDialog.message', { path: cid }),
buttons: [i18n.t('close')]
})

return
}

const dir = await dock.run(() => selectDirectory({
defaultPath: app.getPath('downloads')
}))

if (!dir) {
logger.info(`[cid download] dropping ${path}: user didn't choose a path.`)
return
}

let files

try {
logger.info(`[cid download] downloading ${path}: started`, { withAnalytics: 'DOWNLOAD_HASH' })
files = await ipfsd.api.get(path)
logger.info(`[cid download] downloading ${path}: completed`)
} catch (err) {
logger.error(`[cid download] ${err.stack}`)

showDialog({
title: i18n.t('couldNotGetCidDialog.title'),
message: i18n.t('couldNotGetCidDialog.message', { path }),
buttons: [i18n.t('close')]
})

return
}

try {
await Promise.all(
files
.filter(file => !!file.content)
.map(file => saveFile(dir, file))
)

const opt = showDialog({
title: i18n.t('contentsSavedDialog.title'),
message: i18n.t('contentsSavedDialog.message', { path }),
buttons: [
i18n.t('contentsSavedDialog.action'),
i18n.t('close')
]
})

if (opt === 0) {
shell.showItemInFolder(join(dir, files[0].path))
}
} catch (err) {
logger.error(`[cid download] ${err.toString()}`)

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

module.exports = function (ctx) {
setupGlobalShortcut(ctx, {
settingsOption: CONFIG_KEY,
accelerator: SHORTCUT,
action: () => {
downloadCid(ctx)
}
})
}

module.exports.downloadCid = downloadCid
module.exports.SHORTCUT = SHORTCUT
101 changes: 0 additions & 101 deletions src/download-hash.js

This file was deleted.

4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const setupNpmOnIpfs = require('./npm-on-ipfs')
const setupDaemon = require('./daemon')
const setupWebUI = require('./webui')
const setupAutoLaunch = require('./auto-launch')
const setupDownloadHash = require('./download-hash')

This comment has been minimized.

Copy link
@JpSub7

JpSub7 Mar 1, 2021

1

const setupDownloadCid = require('./download-cid')
const setupTakeScreenshot = require('./take-screenshot')
const setupAppMenu = require('./app-menu')
const setupArgvFilesHandler = require('./argv-files-handler')
Expand Down Expand Up @@ -76,7 +76,7 @@ async function run () {
setupAutoLaunch(ctx),
setupSecondInstance(ctx),
// Setup global shortcuts
setupDownloadHash(ctx),
setupDownloadCid(ctx),
setupTakeScreenshot(ctx),
// Setup PATH-related features
setupNpmOnIpfs(ctx),
Expand Down
71 changes: 71 additions & 0 deletions src/prompt/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const { BrowserWindow, ipcMain, nativeTheme } = require('electron')
const crypto = require('crypto')
const { IS_MAC } = require('../common/consts')
const makePage = require('./template')

const pallette = {
default: {
background: '#ECECEC',
color: '#262626',
inputBackground: '#ffffff',
defaultBackground: '#007AFF'
},
dark: {
background: '#323232',
color: '#ffffff',
inputBackground: '#656565',
defaultBackground: '#0A84FF'
}
}

function generatePage ({ message, defaultValue = '', buttons }, id) {
buttons = buttons.map((txt, i) => `<button ${i === 0 ? 'class="default"' : ''} id="${i}">${txt}</button>`)

if (IS_MAC) {
buttons.reverse()
}

const page = makePage({ pallette, message, defaultValue, buttons, id })
return `data:text/html;base64,${Buffer.from(page, 'utf8').toString('base64')}`
}

module.exports = async function showPrompt (options = {}) {
options.window = options.window || {}

const window = new BrowserWindow({
title: options.title,
show: false,
width: 350,
height: 330,
resizable: false,
autoHideMenuBar: true,
fullscreenable: false,
backgroundColor: nativeTheme.shouldUseDarkColors
? pallette.dark.background
: pallette.default.background,
webPreferences: {
nodeIntegration: true
},
...options.window
})

// Generate random id
const id = crypto.randomBytes(16).toString('hex')

return new Promise(resolve => {
ipcMain.once(id, (_, data) => {
window.destroy()
resolve(data)
})

window.on('close', () => {
resolve({ input: '', button: null })
})

window.once('ready-to-show', () => {
window.show()
})

window.loadURL(generatePage(options, id))
})
}
Loading

0 comments on commit 7188aac

Please sign in to comment.