Skip to content

Commit

Permalink
feat(electron): add basic download functionality (#277)
Browse files Browse the repository at this point in the history
  • Loading branch information
pwambach authored Mar 5, 2020
1 parent 432dfc8 commit 21bad70
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 31 deletions.
38 changes: 23 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"start": "webpack-dev-server",
"clean": "rm -rf ./dist",
"build": "npm run clean && webpack --display=errors-only -p",
"build:electron": "npm run clean && webpack --config webpack.config.electron.js --display=errors-only -p",
"test": "npm run stylint && npm run eslint && npm run license-check",
"eslint": "eslint . --ext .ts,.tsx,.js",
"stylint": "stylint",
Expand All @@ -20,7 +21,7 @@
"electron:start": "electron .",
"electron:install": "electron-builder install-app-deps",
"electron:clean": "rm -rf ./dist-electron",
"electron:build": "npm run electron:clean && npm run electron:install && npm run build && electron-builder -mwl --x64 --config electron-builder.json",
"electron:build": "npm run electron:clean && npm run electron:install && npm run build:electron && electron-builder -mwl --x64 --config electron-builder.json",
"upload-storage": "gsutil rsync -r -x \".DS_Store\" ./storage gs://esa-cfs-storage && gsutil acl ch -r -u AllUsers:R gs://esa-cfs-storage && gsutil setmeta -h \"Cache-Control: no-cache\" gs://esa-cfs-storage/**/*"
},
"repository": {
Expand All @@ -37,6 +38,7 @@
"@types/react-router-dom": "^5.1.3",
"@types/redux-logger": "^3.0.7",
"cesium": "^1.66.0",
"cross-zip": "^3.0.0",
"framer-motion": "^1.8.4",
"lodash.debounce": "^4.0.8",
"react": "^16.12.0",
Expand Down
39 changes: 39 additions & 0 deletions src/electron/download-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const fs = require('fs');
const path = require('path');
const zip = require('cross-zip');
const {app} = require('electron');

/**
* Intercepts all browser downloads in the given window
*/
module.exports.addDownloadHandler = function(browserWindow) {
browserWindow.webContents.session.on('will-download', (_, item) => {
const downloadsPath = app.getPath('downloads');
const tmpFilePath = path.join(downloadsPath, `${Date.now()}.zip`);
item.setSavePath(tmpFilePath);

console.log(`Downloading file ${item.getURL()} to ${item.savePath}`);

item.on('updated', (event, state) => {
if (state === 'interrupted') {
console.log('Download is interrupted but can be resumed');
} else if (state === 'progressing') {
if (item.isPaused()) {
console.log('Download is paused');
} else {
console.log(`Received bytes: ${item.getReceivedBytes()}`);
}
}
});

item.once('done', (event, state) => {
if (state === 'completed') {
console.log('Download successfully', item.savePath);
zip.unzipSync(item.savePath, downloadsPath);
fs.unlinkSync(item.savePath);
} else {
console.log(`Download failed: ${state}`, item.savePath);
}
});
});
};
26 changes: 17 additions & 9 deletions src/electron/main.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
const path = require('path');
const {app, BrowserWindow} = require('electron');
const {addDownloadHandler} = require('./download-handler.js');

// future proof for electron 9 and prevent annoying deprecation warning message
app.allowRendererProcessReuse = true;

let windows = [];

function createWindow() {
// Create the browser window.
// cerate a new browser window
const window = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
preload: path.resolve(__dirname, 'preload.js')
}
width: 1024,
height: 768,
webPreferences: {nodeIntegration: true}
});

// save window's reference
windows.push(window);

// and load the index.html of the app.
// load the index page in the window
const indexPath = `file://${__dirname}/../../dist/index.html`;
window.loadURL(indexPath);

// window.webContents.openDevTools();
window.webContents.openDevTools();

// free window reference when closed
window.on('closed', () => {
windows = windows.filter(w => w !== window);
});

// add download handler
const downloadsPath = path.join(app.getPath('home'), '.esa-cfs', 'offline');
app.setPath('downloads', downloadsPath);
addDownloadHandler(window);
}

app.on('ready', createWindow);
Expand Down
1 change: 0 additions & 1 deletion src/electron/preload.js

This file was deleted.

7 changes: 6 additions & 1 deletion src/scripts/components/layer-list-item/layer-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ interface Props {
layer: LayerListItemType;
isMainSelected: boolean;
onSelect: (id: string, isMain: boolean) => void;
onDownload: null | ((id: string) => void);
}

const LayerListItem: FunctionComponent<Props> = ({
layer,
isMainSelected,
onSelect
onSelect,
onDownload
}) => (
<div className={styles.layerItem} onClick={() => onSelect(layer.id, true)}>
<span className={styles.layerTitle}>{layer.name}</span>
Expand All @@ -28,6 +30,9 @@ const LayerListItem: FunctionComponent<Props> = ({
<FormattedMessage id={'layerSelector.compare'} />
</button>
)}
{onDownload && (
<button onClick={() => onDownload(layer.id)}>Download</button>
)}
</div>
);

Expand Down
5 changes: 4 additions & 1 deletion src/scripts/components/layer-list/layer-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ interface Props {
selectedLayerIds: SelectedLayerIdsState;
layers: LayerListItemType[];
onSelect: (id: string, isMain: boolean) => void;
onDownload: null | ((id: string) => void);
}

const LayerList: FunctionComponent<Props> = ({
selectedLayerIds,
layers,
onSelect
onSelect,
onDownload
}) => {
const {mainId} = selectedLayerIds;
const isMainSelected = Boolean(mainId);
Expand All @@ -30,6 +32,7 @@ const LayerList: FunctionComponent<Props> = ({
<LayerListItem
onSelect={(id, isMain) => onSelect(id, isMain)}
isMainSelected={isMainSelected}
onDownload={onDownload}
layer={layer}
/>
</li>
Expand Down
12 changes: 12 additions & 0 deletions src/scripts/components/layer-selector/layer-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import showLayerSelectorAction from '../../actions/show-layer-selector';
import LayerList from '../layer-list/layer-list';
import SelectedLayerListItem from '../selected-layer-list-item/selected-layer-list-item';
import {layersSelector} from '../../selectors/layers/list';
import {replaceUrlPlaceholders} from '../../libs/replace-url-placeholders';
import config from '../../config/main';
// @ts-ignore
import {isElectron, downloadUrl} from 'electronHelpers'; // this is an webpack alias

import styles from './layer-selector.styl';
import setSelectedLayerIdsAction from '../../actions/set-selected-layer-id';
Expand All @@ -27,6 +31,13 @@ const LayerSelector: FunctionComponent = () => {
layer => layer.id === selectedLayerIds.compareId
);

const onDownload = isElectron()
? (layerId: string) =>
downloadUrl(
replaceUrlPlaceholders(config.api.layerOfflinePackage, {id: layerId})
)
: null;

return (
<AnimatePresence>
{showLayerSelector ? (
Expand Down Expand Up @@ -66,6 +77,7 @@ const LayerSelector: FunctionComponent = () => {
onSelect={(layerId, isMain) =>
dispatch(setSelectedLayerIdsAction(layerId, isMain))
}
onDownload={onDownload}
/>
</div>
</motion.div>
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/config/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export default {
'https://storage.googleapis.com/esa-cfs-tiles/test/{id}/{timeIndex}/{z}/{y}/{x}.png',
layerSingleImage:
'https://storage.googleapis.com/esa-cfs-tiles/test/{id}/{timeIndex}.jpg',
layerOfflinePackage:
'https://storage.googleapis.com/esa-cfs-tiles/generated/{id}/package.zip',
stories:
'https://storage.googleapis.com/esa-cfs-storage/stories/stories-{lang}.json',
story:
Expand Down
7 changes: 7 additions & 0 deletions src/scripts/libs/electron-helpers-mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Webpack will only include this module when building for web
*/

export function isElectron() {
return false;
}
15 changes: 15 additions & 0 deletions src/scripts/libs/electron-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Webpack will only include this module when building for electron
*/

import {remote} from 'electron';

const webContents = remote.getCurrentWebContents();

export function isElectron() {
return true;
}

export function downloadUrl(url: string) {
webContents.downloadURL(url);
}
14 changes: 14 additions & 0 deletions webpack.config.electron.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const path = require('path');
const webConfigFn = require('./webpack.config.js');

module.exports = (env, {mode} = {}) => {
const config = webConfigFn(env, mode);

config.target = 'electron-renderer';
config.resolve.alias.electronHelpers = path.resolve(
__dirname,
'./src/scripts/libs/electron-helpers.ts'
);

return config;
};
14 changes: 11 additions & 3 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ module.exports = (env, {mode} = {}) => {
new OptimizeCSSAssetsPlugin({})
]
},
target: 'web',
resolve: {
extensions: ['.tsx', '.ts', '.js', '.styl'],
alias: {
// use an empty mock module for web
electronHelpers: path.resolve(
__dirname,
'./src/scripts/libs/electron-helpers-mock.ts'
)
}
},
module: {
unknownContextCritical: false,
rules: [
Expand Down Expand Up @@ -79,9 +90,6 @@ module.exports = (env, {mode} = {}) => {
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.styl']
},
output: {
filename: 'bundle.[hash].js',
path: path.resolve(__dirname, 'dist'),
Expand Down

0 comments on commit 21bad70

Please sign in to comment.