diff --git a/package.json b/package.json index a35bddd2b..a45af625f 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@reach/tabs": "^0.1.5", "@reach/tooltip": "^0.2.1", "@types/three": "^0.93.1", + "adm-zip": "^0.4.13", "async-exit-hook": "^2.0.1", "babel-eslint": "^10.0.1", "babel-loader": "^8.0.5", diff --git a/src/ui/component/walletBackup/view.jsx b/src/ui/component/walletBackup/view.jsx index 322cf0dec..73d2d0812 100644 --- a/src/ui/component/walletBackup/view.jsx +++ b/src/ui/component/walletBackup/view.jsx @@ -1,6 +1,9 @@ // @flow import * as React from 'react'; +import { shell, clipboard, remote } from 'electron'; import Button from 'component/button'; +import AdmZip from 'adm-zip'; +import path from 'path'; type Props = { daemonSettings: { @@ -8,7 +11,95 @@ type Props = { }, }; -class WalletBackup extends React.PureComponent { +type State = { + errorMessage: ?string, + successMessage: ?string, +}; + +class WalletBackup extends React.PureComponent { + constructor(props: Props) { + super(props); + + this.state = { + errorMessage: null, + successMessage: null, + }; + } + + showErrorMessage(message: string) { + this.setState({ errorMessage: message }); + } + + showSuccessMessage(message: string) { + this.setState({ successMessage: message }); + } + + flashSuccessMessage(message: string, delay: ?number) { + delay = delay || 2000; + this.showSuccessMessage(message); + setTimeout(() => this.setState({ successMessage: null }), delay, { once: true }); + } + + clearMessages() { + this.setState({ errorMessage: null, successMessage: null }); + } + + backupWalletDir(lbryumWalletDir: ?string) { + this.clearMessages(); + + if (!lbryumWalletDir) { + this.showErrorMessage(__('No wallet folder was found.')); + return; + } + + // Colon fails on Windows. Backups should be portable, so replace it on other platforms, too. + const filenameTime = new Date().toISOString().replace(/:/g, '-'); + + const outputFilename = [path.basename(lbryumWalletDir), '-', filenameTime, '.zip'].join(''); + + // Prefer placing backup in user's Downloads folder, then their home folder, and finally + // right next to the lbryum folder itself. + let outputDir = path.dirname(lbryumWalletDir); + if (remote && remote.app) { + outputDir = remote.app.getPath('downloads') || remote.app.getPath('home') || outputDir; + } + + const outputPath = path.join(outputDir, outputFilename); + + const zip = new AdmZip(); + + try { + zip.addLocalFolder(lbryumWalletDir); + } catch (err) { + console.error(err); + this.showErrorMessage(__('The wallet folder could not be added to the zip archive.')); + return; + } + + try { + zip.writeZip(outputPath); + } catch (err) { + console.error(err); + this.showErrorMessage(__('There was a problem writing the zip archive to disk.')); + return; + } + + this.showSuccessMessage(__('Saved zip archive to ' + outputPath)); + + shell.showItemInFolder(outputPath); + } + + copyWalletDirToClipboard(lbryumWalletDir: ?string) { + this.clearMessages(); + + if (lbryumWalletDir) { + clipboard.writeText(lbryumWalletDir); + this.flashSuccessMessage(__('Copied path to clipboard.')); + } else { + this.showErrorMessage(__('No wallet folder was found.')); + } + } + render() { const { daemonSettings } = this.props; const { wallet_dir: lbryumWalletDir } = daemonSettings; @@ -55,6 +146,25 @@ class WalletBackup extends React.PureComponent {