From 7ffe74eeec9032f48f474d5c4a0472744ec444df Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Mon, 28 May 2018 22:41:24 -0500 Subject: [PATCH 01/13] try improve search performance --- app/actions/bundle.actions.js | 18 +++++++-------- app/actions/bundleFilter.actions.js | 30 +++++++++++++++++++++---- app/components/Bundles.js | 4 ++-- app/constants/bundleFilter.constants.js | 4 ++-- app/reducers/bundlesFilter.reducer.js | 15 ++----------- 5 files changed, 41 insertions(+), 30 deletions(-) diff --git a/app/actions/bundle.actions.js b/app/actions/bundle.actions.js index 4db3703c69..e27e3c9fc4 100644 --- a/app/actions/bundle.actions.js +++ b/app/actions/bundle.actions.js @@ -99,12 +99,12 @@ export function setupBundlesEventSource(authentication) { } }; - function listenStorerExecuteTaskDownloadResources(e) { - console.log(e); + function listenStorerExecuteTaskDownloadResources() { + // console.log(e); } function listenStorerChangeMode(e, dispatch) { - console.log(e); + // console.log(e); const data = JSON.parse(e.data); const bundleId = data.args[0]; const mode = data.args[1]; @@ -121,8 +121,8 @@ export function setupBundlesEventSource(authentication) { }; } - function listenDownloaderReceiver(e) { - console.log(e); + function listenDownloaderReceiver() { + // console.log(e); } /* downloader/status @@ -131,7 +131,7 @@ export function setupBundlesEventSource(authentication) { * 'component': 'downloader', 'type': 'status'}} */ function listenDownloaderStatus(e, dispatch) { - console.log(e); + // console.log(e); const data = JSON.parse(e.data); const bundleId = data.args[0]; const resourcesDownloaded = data.args[1]; @@ -149,7 +149,7 @@ export function setupBundlesEventSource(authentication) { } function listenStorerDeleteResource(e, dispatch) { - console.log(e); + // console.log(e); const data = JSON.parse(e.data); const bundleId = data.args[0]; const resourceToRemove = data.args[1]; @@ -164,8 +164,8 @@ export function setupBundlesEventSource(authentication) { }; } - function listenStorerUpdateFromDownload(e) { - console.log(e); + function listenStorerUpdateFromDownload() { + // console.log(e); } } diff --git a/app/actions/bundleFilter.actions.js b/app/actions/bundleFilter.actions.js index 92efcedb98..53e5e60dec 100644 --- a/app/actions/bundleFilter.actions.js +++ b/app/actions/bundleFilter.actions.js @@ -4,7 +4,7 @@ import { bundleFilterConstants } from '../constants/bundleFilter.constants'; export const bundleFilterActions = { updateSearchInput, - addSearchMatch, + addSearchResults, clearSearch }; @@ -36,6 +36,11 @@ export function updateSearchInput(searchInput, bundles) { textToHighlight}) */ function updateAllSearchMatches(dispatch, searchableBundles, searchKeywords) { + let searchResults = { + chunks: {}, + foundChunks: {}, + bundlesMatching: {}, + }; const chunksAcrossBundles = {}; searchableBundles.forEach((searchableBundle) => { const chunksInBundle = {}; @@ -55,15 +60,32 @@ export function updateSearchInput(searchInput, bundles) { } }); if (Object.keys(matchesInBundle).length > 0) { - dispatch(addSearchMatch(searchableBundle, chunksInBundle, matchesInBundle)); + searchResults = addSearchResults(searchResults, searchableBundle, chunksInBundle, matchesInBundle); } }); + dispatch(updateSearchResults(searchResults)); } } -export function addSearchMatch(bundle, chunks, matches) { +function addSearchResults(searchResults, bundle, chunks, matches) { + const oldBundlesMatching = searchResults.bundlesMatching; + const oldChunks = searchResults.chunks; + const oldMatches = searchResults.matches; + const key = bundle.id; + const newMatchingBundle = { [key]: bundle }; + const newChunks = chunks; + const newMatches = matches; return { - type: bundleFilterConstants.ADD_SEARCH_MATCH, bundle, chunks, matches + bundlesMatching: { ...oldBundlesMatching, ...newMatchingBundle }, + chunks: { ...oldChunks, ...newChunks }, + matches: { ...oldMatches, ...newMatches } + }; +} + + +export function updateSearchResults(searchResults) { + return { + type: bundleFilterConstants.UPDATE_SEARCH_RESULTS, searchResults }; } diff --git a/app/components/Bundles.js b/app/components/Bundles.js index 80b1d18f69..8201a15a06 100644 --- a/app/components/Bundles.js +++ b/app/components/Bundles.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import { DebounceInput } from 'react-debounce-input'; import LinearProgress from 'material-ui/LinearProgress'; @@ -63,7 +63,7 @@ const mapDispatchToProps = { clearSearch }; -class Bundles extends Component { +class Bundles extends PureComponent { props: Props; componentDidMount() { const { history, clearSearch: clearSearchResults } = this.props; diff --git a/app/constants/bundleFilter.constants.js b/app/constants/bundleFilter.constants.js index 100a1b6e11..17955c6046 100644 --- a/app/constants/bundleFilter.constants.js +++ b/app/constants/bundleFilter.constants.js @@ -1,6 +1,6 @@ export const bundleFilterConstants = { - UPDATE_SEARCH_INPUT: 'BUNDLES_FILTER_UPDATE_SEARCH', - ADD_SEARCH_MATCH: 'BUNDLES_FILTER_ADD_SEARCH_MATCH', + UPDATE_SEARCH_INPUT: 'BUNDLES_FILTER_UPDATE_SEARCH_INPUT', + UPDATE_SEARCH_RESULTS: 'BUNDLES_FILTER_UPDATE_SEARCH_RESULTS', CLEAR_SEARCH_RESULTS: 'BUNDLES_FILTER_UPDATE_SEARCH_CLEAR' }; diff --git a/app/reducers/bundlesFilter.reducer.js b/app/reducers/bundlesFilter.reducer.js index bd1a3b7cd1..5ed4a8b382 100644 --- a/app/reducers/bundlesFilter.reducer.js +++ b/app/reducers/bundlesFilter.reducer.js @@ -15,21 +15,10 @@ export function bundlesFilter(state = { isSearchActive: false }, action) { bundlesMatching: {}, } }; - } case bundleFilterConstants.ADD_SEARCH_MATCH: { - const oldBundlesMatching = state.searchResults.bundlesMatching; - const oldChunks = state.searchResults.chunks; - const oldMatches = state.searchResults.matches; - const key = action.bundle.id; - const newMatchingBundle = { [key]: action.bundle }; - const newChunks = action.chunks; - const newMatches = action.matches; + } case bundleFilterConstants.UPDATE_SEARCH_RESULTS: { return { ...state, - searchResults: { - bundlesMatching: { ...oldBundlesMatching, ...newMatchingBundle }, - chunks: { ...oldChunks, ...newChunks }, - matches: { ...oldMatches, ...newMatches } - } + searchResults: { ...action.searchResults } }; } case bundleFilterConstants.CLEAR_SEARCH_RESULTS: return { From f13bd11718bab0c3b075b44e506a61f78c5f3d55 Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Tue, 29 May 2018 11:08:20 -0500 Subject: [PATCH 02/13] improve performance of selecting bundle (toggling tray menu) --- app/actions/bundle.actions.js | 4 ++-- app/components/Bundles.js | 12 ++++++------ app/reducers/bundles.reducer.js | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/actions/bundle.actions.js b/app/actions/bundle.actions.js index e27e3c9fc4..6bb2d54b8c 100644 --- a/app/actions/bundle.actions.js +++ b/app/actions/bundle.actions.js @@ -318,8 +318,8 @@ export function toggleModePauseResume(id) { return { type: bundleConstants.TOGGLE_MODE_PAUSE_RESUME, id }; } -export function toggleSelectBundle(id) { - return { type: bundleConstants.TOGGLE_SELECT, id }; +export function toggleSelectBundle(selectedBundle) { + return { type: bundleConstants.TOGGLE_SELECT, selectedBundle }; } function getMockBundles() { diff --git a/app/components/Bundles.js b/app/components/Bundles.js index 8201a15a06..bbd5254434 100644 --- a/app/components/Bundles.js +++ b/app/components/Bundles.js @@ -92,15 +92,15 @@ class Bundles extends PureComponent { } } - onKeyPress(event, bundleId) { + onKeyPress(event, bundle) { if (['Enter', ' '].includes(event.key)) { - this.onClickBundleRow(event, bundleId); + this.onClickBundleRow(event, bundle); } console.log(event.key); } - onClickBundleRow(event, bundleId) { - this.props.toggleSelectBundle(bundleId); + onClickBundleRow(event, bundle) { + this.props.toggleSelectBundle(bundle); } startSaveBundleTo(event, bundle, savedToHistory) { @@ -185,8 +185,8 @@ class Bundles extends PureComponent {
this.onKeyPress(e, d.id)} - onClick={(e) => this.onClickBundleRow(e, d.id)} + onKeyPress={(e) => this.onKeyPress(e, d)} + onClick={(e) => this.onClickBundleRow(e, d)} tabIndex={0} role="button" style={{ background: `${pickBackgroundColor(d.task, d.status)}` }} diff --git a/app/reducers/bundles.reducer.js b/app/reducers/bundles.reducer.js index 6bf36e2a57..baabdc0624 100644 --- a/app/reducers/bundles.reducer.js +++ b/app/reducers/bundles.reducer.js @@ -92,8 +92,8 @@ export function bundles(state = {}, action) { }; } case bundleConstants.TOGGLE_SELECT: { - const selectedBundle = state.selectedBundle && state.selectedBundle.id === action.id ? - {} : state.items.find(bundle => bundle.id === action.id); + const selectedBundle = state.selectedBundle && state.selectedBundle.id === action.selectedBundle.id ? + {} : action.selectedBundle; return { ...state, selectedBundle From 85d73685e1dd351649e3317829d8f1070cedbb50 Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Tue, 29 May 2018 15:42:44 -0500 Subject: [PATCH 03/13] filter out bundles that don't have metadata.xml downloaded --- app/services/bundle.service.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/services/bundle.service.js b/app/services/bundle.service.js index 5d86143830..dfee320214 100644 --- a/app/services/bundle.service.js +++ b/app/services/bundle.service.js @@ -88,11 +88,11 @@ function fetchAll() { } function convertBundleApiListToBundles(apiBundles) { - const bundles = Object.keys(apiBundles).map((bundleId) => { - const apiBundle = apiBundles[bundleId]; + const bundles = Object.values(apiBundles).filter(apiBundle => apiBundle.metadata).map((apiBundle) => { const { mode, metadata, dbl, store } = apiBundle; + const bundleId = apiBundle.local_id; let task = dbl.currentRevision === '0' ? 'UPLOAD' : 'DOWNLOAD'; let status = dbl.currentRevision === '0' ? 'DRAFT' : 'NOT_STARTED'; if (mode === 'store') { @@ -125,10 +125,9 @@ function convertBundleApiListToBundles(apiBundles) { } } } - const bundleName = metadata && metadata.name ? metadata.name : `loading_name (${dbl.id})`; return { id: bundleId, - name: bundleName, + name: metadata.name, revision: dbl.currentRevision, dblId: dbl.id, medium: dbl.medium, From c63248f79338d9e600d8e16d9a311e1f20801aab Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Tue, 29 May 2018 17:14:52 -0500 Subject: [PATCH 04/13] add sorted-array module --- package.json | 1 + yarn.lock | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/package.json b/package.json index acc9c48b71..876c3689c1 100644 --- a/package.json +++ b/package.json @@ -211,6 +211,7 @@ "react-router-redux": "^5.0.0-alpha.6", "redux": "^3.7.2", "redux-thunk": "^2.2.0", + "sorted-array": "^2.0.2", "source-map-support": "^0.5.3", "split-string": "^5.0.4", "traverse": "^0.6.6" diff --git a/yarn.lock b/yarn.lock index dac771f9c3..bdbcad2603 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9194,6 +9194,10 @@ sort-keys@^1.0.0: dependencies: is-plain-obj "^1.0.0" +sorted-array@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/sorted-array/-/sorted-array-2.0.2.tgz#b55123483a243e965adc79cc14c5160464d91d86" + source-list-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" From b29bc643298ba71c1faa84b3bfbd4eb106397870 Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Tue, 29 May 2018 17:15:29 -0500 Subject: [PATCH 05/13] begin try to add bundle after sse download metadata.xml event --- app/actions/bundle.actions.js | 45 ++++++----- app/constants/bundle.constants.js | 2 + app/reducers/bundles.reducer.js | 9 +++ app/services/bundle.service.js | 129 ++++++++++++++++-------------- 4 files changed, 106 insertions(+), 79 deletions(-) diff --git a/app/actions/bundle.actions.js b/app/actions/bundle.actions.js index 6bb2d54b8c..933081a22e 100644 --- a/app/actions/bundle.actions.js +++ b/app/actions/bundle.actions.js @@ -78,11 +78,11 @@ export function setupBundlesEventSource(authentication) { }; const listeners = { 'storer/execute_task': listenStorerExecuteTaskDownloadResources, - 'storer/change_mode': (e) => listenStorerChangeMode(e, dispatch), + 'storer/change_mode': listenStorerChangeMode, 'downloader/receiver': listenDownloaderReceiver, 'downloader/status': (e) => listenDownloaderStatus(e, dispatch), 'storer/delete_resource': (e) => listenStorerDeleteResource(e, dispatch), - 'storer/update_from_download': listenStorerUpdateFromDownload, + 'storer/update_from_download': (e) => listenStorerUpdateFromDownload(e, dispatch), }; Object.keys(listeners).forEach((evType) => { const handler = listeners[evType]; @@ -103,22 +103,14 @@ export function setupBundlesEventSource(authentication) { // console.log(e); } - function listenStorerChangeMode(e, dispatch) { + function listenStorerChangeMode() { // console.log(e); - const data = JSON.parse(e.data); - const bundleId = data.args[0]; - const mode = data.args[1]; - if (mode === 'store') { - dispatch(updateStatus(bundleId, 'COMPLETED')); - } - } - - function updateStatus(_id, status) { - return { - type: bundleConstants.UPDATE_STATUS, - id: _id, - status, - }; + // const data = JSON.parse(e.data); + // const bundleId = data.args[0]; + // const mode = data.args[1]; + // if (mode === 'store') { + // dispatch(updateStatus(bundleId, 'COMPLETED')); + // } } function listenDownloaderReceiver() { @@ -164,8 +156,23 @@ export function setupBundlesEventSource(authentication) { }; } - function listenStorerUpdateFromDownload() { - // console.log(e); + async function listenStorerUpdateFromDownload(e, dispatch) { + const data = JSON.parse(e.data); + const bundleId = data.args[0]; + const apiBundle = await bundleService.fetchById(bundleId); + const fileInfoKeys = Object.keys(apiBundle.store.file_info); + if (fileInfoKeys.length === 1 && fileInfoKeys[0] === 'metadata.xml') { + // we just downloaded metadata.xml + const bundle = bundleService.convertApiBundleToNathanaelBundle(apiBundle); + dispatch(addBundle(bundle)); + } + } + + function addBundle(bundle) { + return { + type: bundleConstants.ADD_BUNDLE, + bundle + }; } } diff --git a/app/constants/bundle.constants.js b/app/constants/bundle.constants.js index 36f4d035cd..0d39429aa6 100644 --- a/app/constants/bundle.constants.js +++ b/app/constants/bundle.constants.js @@ -15,6 +15,8 @@ export const bundleConstants = { DELETE_SUCCESS: 'BUNDLES_DELETE_SUCCESS', DELETE_FAILURE: 'BUNDLES_DELETE_FAILURE', + ADD_BUNDLE: 'BUNDLES_ADD_BUNDLE', + UPDATE_STATUS: 'BUNDLES_UPDATE_STATUS', SESSION_EVENTS_CONNECTED: 'BUNDLES_SESSION_EVENTS_CONNECTED', diff --git a/app/reducers/bundles.reducer.js b/app/reducers/bundles.reducer.js index baabdc0624..e6fca88735 100644 --- a/app/reducers/bundles.reducer.js +++ b/app/reducers/bundles.reducer.js @@ -1,3 +1,4 @@ +import SortedArray from 'sorted-array'; import { bundleConstants } from '../constants/bundle.constants'; export function bundles(state = {}, action) { @@ -28,6 +29,14 @@ export function bundles(state = {}, action) { ? { ...bundle, deleting: true } : bundle)) }; + case bundleConstants.ADD_BUNDLE: { + const { bundle } = action; + const sorted = SortedArray.comparing((b) => b.name, [...state.items, bundle]); + return { + ...state, + items: sorted.array + }; + } case bundleConstants.DOWNLOAD_RESOURCES_REQUEST: { return updateTaskStatusProgress(action.id, 'DOWNLOAD', 'IN_PROGRESS', 0); } diff --git a/app/services/bundle.service.js b/app/services/bundle.service.js index dfee320214..e26ec4caed 100644 --- a/app/services/bundle.service.js +++ b/app/services/bundle.service.js @@ -3,12 +3,12 @@ import { authHeader } from '../helpers'; import { dblDotLocalConfig } from '../constants/dblDotLocal.constants'; import download from './download-with-fetch.flow'; - export const bundleService = { create, fetchAll, fetchById, update, + convertApiBundleToNathanaelBundle, getManifestResourcePaths, downloadResources, removeResources, @@ -83,69 +83,81 @@ function fetchAll() { method: 'GET', headers: authHeader() }; - return fetch(`${dblDotLocalConfig.getHttpDblDotLocalBaseUrl()}/${BUNDLE_API_LIST}`, requestOptions) - .then(handleResponse).then(convertBundleApiListToBundles); + return fetch( + `${dblDotLocalConfig.getHttpDblDotLocalBaseUrl()}/${BUNDLE_API_LIST}`, + requestOptions + ) + .then(handleResponse) + .then(convertBundleApiListToBundles); } function convertBundleApiListToBundles(apiBundles) { - const bundles = Object.values(apiBundles).filter(apiBundle => apiBundle.metadata).map((apiBundle) => { - const { - mode, metadata, dbl, store - } = apiBundle; - const bundleId = apiBundle.local_id; - let task = dbl.currentRevision === '0' ? 'UPLOAD' : 'DOWNLOAD'; - let status = dbl.currentRevision === '0' ? 'DRAFT' : 'NOT_STARTED'; - if (mode === 'store') { - const { history } = store; - const historyReversed = history.slice().reverse(); - const eventUpdateStore = historyReversed.find((event) => event.type === 'updateStore'); - if (eventUpdateStore && eventUpdateStore.message && eventUpdateStore.message === 'download') { - const indexOfDownloadResources = historyReversed.findIndex((event) => event.type === 'executeTask' && event.message === 'downloadResources'); - const indexOfUpdateStoreDownload = historyReversed.indexOf(eventUpdateStore); - const indexOfRemoveLocalResources = historyReversed.findIndex((event) => event.type === 'executeTask' && event.message === 'removeLocalResources'); - const indexOfChangeModeStore = historyReversed.findIndex((event) => event.type === 'changeMode' && event.message === 'store'); - if (indexOfRemoveLocalResources !== -1 - && indexOfRemoveLocalResources < indexOfDownloadResources) { - if (indexOfChangeModeStore !== -1 - && indexOfChangeModeStore < indexOfDownloadResources) { - task = 'DOWNLOAD'; - status = 'NOT_STARTED'; - } else { - task = 'REMOVE_RESOURCES'; - status = 'IN_PROGRESS'; - } - } else if (indexOfDownloadResources !== -1) { + const bundles = Object.values(apiBundles) + .filter(apiBundle => apiBundle.metadata) + .map(apiBundle => convertApiBundleToNathanaelBundle(apiBundle)); + return bundles; +} + +function convertApiBundleToNathanaelBundle(apiBundle) { + const { + mode, metadata, dbl, store + } = apiBundle; + const bundleId = apiBundle.local_id; + let task = dbl.currentRevision === '0' ? 'UPLOAD' : 'DOWNLOAD'; + let status = dbl.currentRevision === '0' ? 'DRAFT' : 'NOT_STARTED'; + if (mode === 'store') { + const { history } = store; + const historyReversed = history.slice().reverse(); + const eventUpdateStore = historyReversed.find(event => event.type === 'updateStore'); + if (eventUpdateStore && eventUpdateStore.message && eventUpdateStore.message === 'download') { + const indexOfDownloadResources = historyReversed.findIndex(event => event.type === 'executeTask' && event.message === 'downloadResources'); + const indexOfUpdateStoreDownload = historyReversed.indexOf(eventUpdateStore); + const indexOfRemoveLocalResources = historyReversed.findIndex(event => event.type === 'executeTask' && event.message === 'removeLocalResources'); + const indexOfChangeModeStore = historyReversed.findIndex(event => event.type === 'changeMode' && event.message === 'store'); + if ( + indexOfRemoveLocalResources !== -1 && + indexOfRemoveLocalResources < indexOfDownloadResources + ) { + if (indexOfChangeModeStore !== -1 && indexOfChangeModeStore < indexOfDownloadResources) { task = 'DOWNLOAD'; - if (indexOfUpdateStoreDownload !== -1 - && indexOfUpdateStoreDownload < indexOfDownloadResources) { - status = 'COMPLETED'; - } else { - status = 'IN_PROGRESS'; - } + status = 'NOT_STARTED'; + } else { + task = 'REMOVE_RESOURCES'; + status = 'IN_PROGRESS'; + } + } else if (indexOfDownloadResources !== -1) { + task = 'DOWNLOAD'; + if ( + indexOfUpdateStoreDownload !== -1 && + indexOfUpdateStoreDownload < indexOfDownloadResources + ) { + status = 'COMPLETED'; + } else { + status = 'IN_PROGRESS'; } } } - return { - id: bundleId, - name: metadata.name, - revision: dbl.currentRevision, - dblId: dbl.id, - medium: dbl.medium, - task, - status, - }; - }); - return bundles; + } + return { + id: bundleId, + name: metadata.name, + revision: dbl.currentRevision, + dblId: dbl.id, + medium: dbl.medium, + task, + status + }; } - function fetchById(id) { const requestOptions = { method: 'GET', headers: authHeader() }; - return fetch(`${dblDotLocalConfig.getHttpDblDotLocalBaseUrl()}/${BUNDLE_API}/${id}`, requestOptions) - .then(handleResponse); + return fetch( + `${dblDotLocalConfig.getHttpDblDotLocalBaseUrl()}/${BUNDLE_API}/${id}`, + requestOptions + ).then(handleResponse); } function create(bundle) { @@ -155,8 +167,10 @@ function create(bundle) { body: JSON.stringify(bundle) }; - return fetch(`${dblDotLocalConfig.getHttpDblDotLocalBaseUrl()}/${BUNDLE_API}/create`, requestOptions) - .then(handleResponse); + return fetch( + `${dblDotLocalConfig.getHttpDblDotLocalBaseUrl()}/${BUNDLE_API}/create`, + requestOptions + ).then(handleResponse); } function update(bundle) { @@ -201,8 +215,7 @@ function getManifestResourcePaths(bundleId) { headers: authHeader() }; const url = `${dblDotLocalConfig.getHttpDblDotLocalBaseUrl()}/${BUNDLE_API}/${bundleId}/manifest-resource`; - return fetch(url, requestOptions) - .then(handleResponse); + return fetch(url, requestOptions).then(handleResponse); } function downloadResources(bundleId) { @@ -220,19 +233,16 @@ function bundleAddTasks(bundleId, innerTasks) { body: `xml= ${innerTasks} ` }; const url = `${dblDotLocalConfig.getHttpDblDotLocalBaseUrl()}/${BUNDLE_API}/${bundleId}/add-tasks`; - return fetch(url, requestOptions) - .then(handleTextResponse); + return fetch(url, requestOptions).then(handleTextResponse); } - function getResourcePaths(bundleId) { const requestOptions = { method: 'GET', headers: authHeader() }; const url = `${dblDotLocalConfig.getHttpDblDotLocalBaseUrl()}/${BUNDLE_API}/${bundleId}/${RESOURCE_API_LIST}`; - return fetch(url, requestOptions) - .then(handleResponse); + return fetch(url, requestOptions).then(handleResponse); } /* @@ -244,4 +254,3 @@ function requestSaveResourceTo(selectedFolder, bundleId, resourcePath, progressC const targetPath = path.join(selectedFolder, resourcePath); return download(url, targetPath, progressCallback, authHeader()); } - From beed09cf72dc469f61d6e2b23709fee4a265cafc Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Tue, 29 May 2018 22:07:48 -0500 Subject: [PATCH 06/13] decorate bundle with displayAs info --- app/reducers/bundles.reducer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/reducers/bundles.reducer.js b/app/reducers/bundles.reducer.js index e6fca88735..2f2bd7472c 100644 --- a/app/reducers/bundles.reducer.js +++ b/app/reducers/bundles.reducer.js @@ -31,7 +31,8 @@ export function bundles(state = {}, action) { }; case bundleConstants.ADD_BUNDLE: { const { bundle } = action; - const sorted = SortedArray.comparing((b) => b.name, [...state.items, bundle]); + const decoratedBundle = addBundleDecorators(bundle); + const sorted = SortedArray.comparing((b) => b.name, [...state.items, decoratedBundle]); return { ...state, items: sorted.array From 4749d94264efe411a74fc8d5a5ce4fc48b0f6b41 Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Tue, 29 May 2018 22:49:14 -0500 Subject: [PATCH 07/13] make sure state doesn't change if bundle is not found in list --- app/reducers/bundles.reducer.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/app/reducers/bundles.reducer.js b/app/reducers/bundles.reducer.js index 2f2bd7472c..30387c9fe3 100644 --- a/app/reducers/bundles.reducer.js +++ b/app/reducers/bundles.reducer.js @@ -8,12 +8,16 @@ export function bundles(state = {}, action) { ...state, loading: true }; - case bundleConstants.FETCH_SUCCESS: + case bundleConstants.FETCH_SUCCESS: { + const items = action.bundles.map(bundle => addBundleDecorators(bundle)); + const sorted = SortedArray.comparing((b) => b.name, items); return { ...state, - items: action.bundles.map(bundle => addBundleDecorators(bundle)), + items, + sorted, loading: false, }; + } case bundleConstants.FETCH_FAILURE: return { ...state, @@ -32,10 +36,12 @@ export function bundles(state = {}, action) { case bundleConstants.ADD_BUNDLE: { const { bundle } = action; const decoratedBundle = addBundleDecorators(bundle); - const sorted = SortedArray.comparing((b) => b.name, [...state.items, decoratedBundle]); + const { sorted } = state; + sorted.insert(decoratedBundle); return { ...state, - items: sorted.array + items: sorted.array, + sorted }; } case bundleConstants.DOWNLOAD_RESOURCES_REQUEST: { @@ -142,6 +148,10 @@ export function bundles(state = {}, action) { } function updateTaskStatusProgress(bundleId, task, status, progress, updateDecorators) { + const foundBundle = state.items.find(bundle => bundle.id === bundleId); + if (!foundBundle) { + return state; + } const items = state.items.map(bundle => (bundle.id === bundleId ? addBundleDecorators({ ...bundle, From 4d82b2b58db04169396c7840970713a1559e8860 Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Wed, 30 May 2018 16:37:51 -0500 Subject: [PATCH 08/13] add/refactor to component DBLEntryRow (cherry picked from commit dbb1622fa95e705b600fe17a0067a655ee0a3db8) --- app/components/DBLEntryRow.css | 68 ++++++++ app/components/DBLEntryRow.js | 302 +++++++++++++++++++++++++++++++++ 2 files changed, 370 insertions(+) create mode 100644 app/components/DBLEntryRow.css create mode 100644 app/components/DBLEntryRow.js diff --git a/app/components/DBLEntryRow.css b/app/components/DBLEntryRow.css new file mode 100644 index 0000000000..bf9d54557d --- /dev/null +++ b/app/components/DBLEntryRow.css @@ -0,0 +1,68 @@ +.bundleRow { + width: 100%; + margin-bottom: 2px; +} + +/* override .mark in bootstrap.css */ +.Highlight { + background-color: inherit; + font-weight: bold; + padding: 0; +} + +.bundleRowTop { + display: table; + width: 100%; + vertical-align: middle; + height: 50px; +} + +.bundleRowTopLeftSide { + display: table-cell; + padding-left: 20px; + vertical-align: middle; + width: 33%; +} + +.bundleRowTopMiddle { + display: table-cell; + padding-left: 20px; + vertical-align: middle; + width: 150px; +} + +.bundleRowTopRightSide { + text-align: right; + padding-top: 6px; +/* + padding-right: 20px; + vertical-align: middle; + display: inline-flex; + float: right; + */ +} + +.iconRightOfText { + padding-left: 5px; +} + +.bundleRowBottom { + display: table; + width: 100%; + vertical-align: middle; + height: 30px; +} + +.menuBar { + padding-left: 30px; +} + +.bundleRowBottomMenuItem { + vertical-align: middle; + text-align: left; + font-size: small; + padding-left: 5px; + padding-right: 5px; + padding-bottom: 10px; + display: inline-flex; +} \ No newline at end of file diff --git a/app/components/DBLEntryRow.js b/app/components/DBLEntryRow.js new file mode 100644 index 0000000000..232f98cac4 --- /dev/null +++ b/app/components/DBLEntryRow.js @@ -0,0 +1,302 @@ +import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; +import LinearProgress from 'material-ui/LinearProgress'; +import Highlighter from 'react-highlight-words'; +import FlatButton from 'material-ui/FlatButton'; +import FileDownload from 'material-ui/svg-icons/file/file-download'; +import FolderOpen from 'material-ui/svg-icons/file/folder-open'; +import SaveTo from 'material-ui/svg-icons/content/save'; +import CallSplit from 'material-ui/svg-icons/communication/call-split'; +import ActionInfo from 'material-ui/svg-icons/action/info'; +import ActionDelete from 'material-ui/svg-icons/action/delete'; +import styles from './DBLEntryRow.css'; +import { toggleSelectBundle, requestSaveBundleTo, removeResources, downloadResources } from '../actions/bundle.actions'; + +const { dialog, app } = require('electron').remote; +const { shell } = require('electron'); + +type Props = { + bundleId: string, + dblId: string, + task: string, + status: string, + displayAs: {}, + bundlesFilter: {}, + bundlesSaveTo: {}, + progress: ?integer, + isDownloaded: ?boolean, + isSelected: ?boolean, + toggleSelectBundle: () => {}, + downloadResources: () => {}, + requestSaveBundleTo: () => {}, + removeResources: () => {} +}; + +const mapDispatchToProps = { + toggleSelectBundle, + downloadResources, + requestSaveBundleTo, + removeResources +}; + +function mapStateToProps(state) { + const { bundlesFilter, bundlesSaveTo } = state; + return { + bundlesFilter, + bundlesSaveTo + }; +} + +class DBLEntryRow extends PureComponent { + props: Props; + + onKeyPress(event) { + if (['Enter', ' '].includes(event.key)) { + this.onClickBundleRow(); + } + console.log(event.key); + } + + onClickBundleRow() { + const { bundleId: id, displayAs } = this.props; + this.props.toggleSelectBundle({ id, displayAs }); + } + + showStatusAsText() { + const { task, status } = this.props; + return ((task === 'UPLOAD' || task === 'DOWNLOAD') && + (status === 'COMPLETED' || status === 'DRAFT' || status === 'IN_PROGRESS')) || + ((task === 'REMOVE_RESOURCES') && status === 'IN_PROGRESS'); + } + + showDownloadButton() { + const { task, status } = this.props; + return (task === 'DOWNLOAD' && status === 'NOT_STARTED'); + } + + hasNotYetDownloadedResources() { + const { isDownloaded, progress } = this.props; + return ((isDownloaded === undefined || !isDownloaded) + || (progress && progress < 100)) === true; + } + + updateMatches(options) { + const { bundlesFilter, bundleId } = this.props; + if (!bundlesFilter.isSearchActive) { + return []; + } + const { searchResults } = bundlesFilter; + const { bundlesMatching, chunks } = searchResults; + const hasMatchInBundle = bundleId in bundlesMatching; + if (hasMatchInBundle) { + return chunks[options.textToHighlight]; + } + return []; + } + + getHighlighterSharedProps() { + const { bundlesFilter, emptySearchWords } = this.props; + return { + searchWords: bundlesFilter.isSearchActive ? bundlesFilter.searchKeywords : emptySearchWords, + highlightClassName: styles.Highlight, + findChunks: (options) => this.updateMatches(options) + }; + } + + onClickDownloadResources(event, bundleId) { + this.props.downloadResources(bundleId); + event.stopPropagation(); + } + + startSaveBundleTo(event) { + const { bundlesSaveTo: savedToHistory, bundleId } = this.props; + stopPropagation(event); + const bundleSavedToInfo = getBundleExportInfo(bundleId, savedToHistory); + const defaultPath = bundleSavedToInfo ? bundleSavedToInfo.folderName : app.getPath('downloads'); + dialog.showOpenDialog({ + defaultPath, + properties: ['openDirectory'] + }, (folderName) => { + if (!folderName) { + return; // canceled. + } + console.log(folderName.toString()); + this.props.requestSaveBundleTo(bundleId, folderName.toString()); + }); + } + + openInFolder(event) { + const { bundlesSaveTo: savedToHistory, bundleId } = this.props; + event.stopPropagation(); + const bundleSavedToInfo = getBundleExportInfo(bundleId, savedToHistory); + if (bundleSavedToInfo) { + const { folderName } = bundleSavedToInfo; + shell.openItem(folderName); + } + } + + onOpenDBLEntryLink(event) { + const { dblId } = this.props; + onOpenLink(event, `https://thedigitalbiblelibrary.org/entry?id=${dblId}`); + } + + onClickRemoveResources(event) { + const { bundleId } = this.props; + this.props.removeResources(bundleId); + event.stopPropagation(); + } + + + render() { + const { + bundleId, + dblId, + task, + status, + displayAs, + progress, + isSelected + } = this.props; + return ( +
+
+
+ +
+
+ +
+
+ {task === 'SAVETO' && ( + + } + icon={} + onClick={this.openInFolder} + /> + )} + {this.showStatusAsText() && ( +
+ +
+ )} + {this.showDownloadButton() && ( + + } + icon={} + onClick={this.onClickDownloadResources} + /> + )} +
+
+ {status === 'IN_PROGRESS' && ( +
+ +
+ )} + {isSelected && ( +
+ } + disabled + onKeyPress={stopPropagation} + onClick={stopPropagation} + /> + } + onKeyPress={this.startSaveBundleTo} + onClick={this.startSaveBundleTo} + /> + } + onKeyPress={this.onOpenDBLEntryLink} + onClick={this.onOpenDBLEntryLink} + /> + } + onKeyPress={this.onClickRemoveResources} + onClick={this.onClickRemoveResources} + /> +
+ )} +
+ ); + } +} + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(DBLEntryRow); + +function getBundleExportInfo(bundleId, savedToHistory) { + return savedToHistory ? savedToHistory[bundleId] : null; +} + +function pickBackgroundColor(task, status) { + if (task === 'SAVETO') { + return '#FFE793'; + } + switch (status) { + case 'DRAFT': return '#F5D2D2'; + case 'NOT_STARTED': return '#EDEDED'; + case 'IN_PROGRESS': + return '#6DCBC4'; + case 'COMPLETED': return '#A1CB6D'; + default: + return 'white'; + } +} + +function stopPropagation(event) { + event.stopPropagation(); +} + +function onOpenLink(event, url) { + event.preventDefault(); + event.stopPropagation(); + shell.openExternal(url); +} + +DBLEntryRow.propTypes = { + emptySearchWords: [] +}; + +DBLEntryRow.defaultProps = { + emptySearchWords: [] +}; From 5a888cc99d6b206ab3b3b71394794c27e0c79494 Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Wed, 30 May 2018 16:59:53 -0500 Subject: [PATCH 09/13] try hooking up DBLEntryRow (cherry picked from commit 0836105d64104f60c709b02814166193f97dbaa7) # Conflicts: # app/components/Bundles.js --- app/components/Bundles.js | 229 ++-------------------------------- app/components/DBLEntryRow.js | 11 +- 2 files changed, 11 insertions(+), 229 deletions(-) diff --git a/app/components/Bundles.js b/app/components/Bundles.js index bbd5254434..451d7e4120 100644 --- a/app/components/Bundles.js +++ b/app/components/Bundles.js @@ -1,51 +1,31 @@ import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import { DebounceInput } from 'react-debounce-input'; -import LinearProgress from 'material-ui/LinearProgress'; import CircularProgress from 'material-ui/CircularProgress'; -import Highlighter from 'react-highlight-words'; -import FlatButton from 'material-ui/FlatButton'; -import FileDownload from 'material-ui/svg-icons/file/file-download'; -import FolderOpen from 'material-ui/svg-icons/file/folder-open'; -import SaveTo from 'material-ui/svg-icons/content/save'; -import CallSplit from 'material-ui/svg-icons/communication/call-split'; -import ActionInfo from 'material-ui/svg-icons/action/info'; -import ActionDelete from 'material-ui/svg-icons/action/delete'; import { navigationConstants } from '../constants/navigation.constants'; +import DBLEntryRow from './DBLEntryRow'; import { mockFetchAll, fetchAll, - toggleSelectBundle, toggleModePauseResume, - setupBundlesEventSource, downloadResources, requestSaveBundleTo, - removeResources } from '../actions/bundle.actions'; + setupBundlesEventSource } from '../actions/bundle.actions'; import { updateSearchInput, clearSearch } from '../actions/bundleFilter.actions'; import styles from './Bundles.css'; -const { dialog, app } = require('electron').remote; -const { shell } = require('electron'); - type Props = { fetchAll: () => {}, mockFetchAll: () => {}, - downloadResources: () => {}, setupBundlesEventSource: () => {}, - requestSaveBundleTo: () => {}, - removeResources: () => {}, - toggleSelectBundle: () => {}, - toggleModePauseResume: () => {}, updateSearchInput: () => {}, clearSearch: () => {}, history: {}, bundles: {}, bundlesFilter: {}, - bundlesSaveTo: {}, authentication: {} }; function mapStateToProps(state) { - const { bundles, bundlesFilter, bundlesSaveTo, authentication } = state; + const { bundles, bundlesFilter, authentication } = state; return { bundles, bundlesFilter, - bundlesSaveTo, authentication }; } @@ -54,11 +34,6 @@ const mapDispatchToProps = { fetchAll, mockFetchAll, setupBundlesEventSource, - downloadResources, - requestSaveBundleTo, - removeResources, - toggleSelectBundle, - toggleModePauseResume, updateSearchInput, clearSearch }; @@ -92,78 +67,12 @@ class Bundles extends PureComponent { } } - onKeyPress(event, bundle) { - if (['Enter', ' '].includes(event.key)) { - this.onClickBundleRow(event, bundle); - } - console.log(event.key); - } - - onClickBundleRow(event, bundle) { - this.props.toggleSelectBundle(bundle); - } - - startSaveBundleTo(event, bundle, savedToHistory) { - stopPropagation(event); - const bundleSavedToInfo = getBundleExportInfo(bundle, savedToHistory); - const defaultPath = bundleSavedToInfo ? bundleSavedToInfo.folderName : app.getPath('downloads'); - dialog.showOpenDialog({ - defaultPath, - properties: ['openDirectory'] - }, (folderName) => { - if (!folderName) { - return; // canceled. - } - console.log(folderName.toString()); - this.props.requestSaveBundleTo(bundle.id, folderName.toString()); - }); - } - - onClickDownloadResources(event, bundleId) { - this.props.downloadResources(bundleId); - event.stopPropagation(); - } - - onClickRemoveResources(event, bundle) { - this.props.removeResources(bundle.id); - event.stopPropagation(); - } - - onClickTogglePauseResume(event, bundleId) { - this.props.toggleModePauseResume(bundleId); - event.stopPropagation(); - } - onChangeSearchInput(event, inputValue) { this.props.updateSearchInput(inputValue, this.props.bundles); } - updateMatches(bundle, options) { - const { bundlesFilter } = this.props; - if (!bundlesFilter.isSearchActive) { - return []; - } - const { searchResults } = bundlesFilter; - const { bundlesMatching, chunks } = searchResults; - const hasMatchInBundle = bundle.id in bundlesMatching; - if (hasMatchInBundle) { - return chunks[options.textToHighlight]; - } - return []; - } - render() { - const { bundles, bundlesFilter, bundlesSaveTo } = this.props; - const { savedToHistory } = bundlesSaveTo; - const highlighterSharedProps = (bundle) => ({ - searchWords: bundlesFilter.isSearchActive ? bundlesFilter.searchKeywords : [], - highlightClassName: styles.Highlight, - findChunks: (options) => this.updateMatches(bundle, options) - }); - const onClickAndKeyPressProps = (handler) => ({ - onKeyPress: handler, - onClick: handler, - }); + const { bundles, bundlesFilter } = this.props; return (
@@ -182,79 +91,12 @@ class Bundles extends PureComponent {
} {bundles.items && bundles.items.filter((b) => displayRow(bundlesFilter, b)).map((d) => ( -
this.onKeyPress(e, d)} - onClick={(e) => this.onClickBundleRow(e, d)} - tabIndex={0} - role="button" - style={{ background: `${pickBackgroundColor(d.task, d.status)}` }} - > -
-
- -
-
- -
-
- {d.task === 'SAVETO' && - (} - icon={} - onClick={(e) => openInFolder(e, d, savedToHistory)} - />) - } - {showStatusAsText(d) && -
- -
} - {showDownloadButton(d) && - } - icon={} - onClick={(e) => this.onClickDownloadResources(e, d.id)} - /> - } -
-
- {d.status === 'IN_PROGRESS' && -
- -
} - {bundles.selectedBundle && bundles.selectedBundle.id === d.id && -
- } - disabled - onKeyPress={(e) => stopPropagation(e)} - onClick={(e) => stopPropagation(e)} - /> - } - {...onClickAndKeyPressProps((e) => this.startSaveBundleTo(e, d, savedToHistory))} - /> - } - {...onClickAndKeyPressProps((e) => onOpenLink(e, `https://thedigitalbiblelibrary.org/entry?id=${d.dblId}`))} - /> - } - {...onClickAndKeyPressProps((e) => this.onClickRemoveResources(e, d))} - /> -
- } -
))} + bundleId={d.id} + {...d} + isSelected={bundles.selectedBundle && bundles.selectedBundle.id === d.id} + />))}
); } @@ -265,60 +107,7 @@ export default connect( mapDispatchToProps, )(Bundles); -function hasNotYetDownloadedResources(bundle) { - return ((bundle.isDownloaded === undefined || !bundle.isDownloaded) - || (bundle.progress && bundle.progress < 100)) === true; -} - -function showStatusAsText(bundle) { - return ((bundle.task === 'UPLOAD' || bundle.task === 'DOWNLOAD') && - (bundle.status === 'COMPLETED' || bundle.status === 'DRAFT' || bundle.status === 'IN_PROGRESS')) || - ((bundle.task === 'REMOVE_RESOURCES') && bundle.status === 'IN_PROGRESS'); -} - -function showDownloadButton(bundle) { - return (bundle.task === 'DOWNLOAD' && bundle.status === 'NOT_STARTED'); -} - function displayRow(bundlesFilter, bundle) { return !(bundlesFilter.isSearchActive) || bundle.id in bundlesFilter.searchResults.bundlesMatching; } - -function getBundleExportInfo(bundle, savedToHistory) { - return savedToHistory ? savedToHistory[bundle.id] : null; -} - -function onOpenLink(event, url) { - event.preventDefault(); - event.stopPropagation(); - shell.openExternal(url); -} - -function openInFolder(event, bundle, savedToHistory) { - stopPropagation(event); - const bundleSavedToInfo = getBundleExportInfo(bundle, savedToHistory); - if (bundleSavedToInfo) { - const { folderName } = bundleSavedToInfo; - shell.openItem(folderName); - } -} - -function stopPropagation(event) { - event.stopPropagation(); -} - -function pickBackgroundColor(task, status) { - if (task === 'SAVETO') { - return '#FFE793'; - } - switch (status) { - case 'DRAFT': return '#F5D2D2'; - case 'NOT_STARTED': return '#EDEDED'; - case 'IN_PROGRESS': - return '#6DCBC4'; - case 'COMPLETED': return '#A1CB6D'; - default: - return 'white'; - } -} diff --git a/app/components/DBLEntryRow.js b/app/components/DBLEntryRow.js index 232f98cac4..1af56ddaa5 100644 --- a/app/components/DBLEntryRow.js +++ b/app/components/DBLEntryRow.js @@ -95,9 +95,9 @@ class DBLEntryRow extends PureComponent { } getHighlighterSharedProps() { - const { bundlesFilter, emptySearchWords } = this.props; + const { bundlesFilter } = this.props; return { - searchWords: bundlesFilter.isSearchActive ? bundlesFilter.searchKeywords : emptySearchWords, + searchWords: bundlesFilter.isSearchActive ? bundlesFilter.searchKeywords : [], highlightClassName: styles.Highlight, findChunks: (options) => this.updateMatches(options) }; @@ -293,10 +293,3 @@ function onOpenLink(event, url) { shell.openExternal(url); } -DBLEntryRow.propTypes = { - emptySearchWords: [] -}; - -DBLEntryRow.defaultProps = { - emptySearchWords: [] -}; From b62a661abffdc36055ff9f722707ee44126200c0 Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Wed, 30 May 2018 21:22:12 -0500 Subject: [PATCH 10/13] improve performance of DBLEntryRow (cherry picked from commit b8866ca48815de696f425d0a21367002c5da247a) --- app/components/DBLEntryRow.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/app/components/DBLEntryRow.js b/app/components/DBLEntryRow.js index 1af56ddaa5..1ae8ede1ed 100644 --- a/app/components/DBLEntryRow.js +++ b/app/components/DBLEntryRow.js @@ -23,7 +23,7 @@ type Props = { displayAs: {}, bundlesFilter: {}, bundlesSaveTo: {}, - progress: ?integer, + progress?: ?number, isDownloaded: ?boolean, isSelected: ?boolean, toggleSelectBundle: () => {}, @@ -50,37 +50,37 @@ function mapStateToProps(state) { class DBLEntryRow extends PureComponent { props: Props; - onKeyPress(event) { + onKeyPress = (event) => { if (['Enter', ' '].includes(event.key)) { this.onClickBundleRow(); } console.log(event.key); } - onClickBundleRow() { + onClickBundleRow = () => { const { bundleId: id, displayAs } = this.props; this.props.toggleSelectBundle({ id, displayAs }); } - showStatusAsText() { + showStatusAsText = () => { const { task, status } = this.props; return ((task === 'UPLOAD' || task === 'DOWNLOAD') && (status === 'COMPLETED' || status === 'DRAFT' || status === 'IN_PROGRESS')) || ((task === 'REMOVE_RESOURCES') && status === 'IN_PROGRESS'); } - showDownloadButton() { + showDownloadButton = () => { const { task, status } = this.props; return (task === 'DOWNLOAD' && status === 'NOT_STARTED'); } - hasNotYetDownloadedResources() { + hasNotYetDownloadedResources = () => { const { isDownloaded, progress } = this.props; return ((isDownloaded === undefined || !isDownloaded) || (progress && progress < 100)) === true; } - updateMatches(options) { + updateMatches = (options) => { const { bundlesFilter, bundleId } = this.props; if (!bundlesFilter.isSearchActive) { return []; @@ -94,21 +94,22 @@ class DBLEntryRow extends PureComponent { return []; } - getHighlighterSharedProps() { + getHighlighterSharedProps = () => { const { bundlesFilter } = this.props; return { searchWords: bundlesFilter.isSearchActive ? bundlesFilter.searchKeywords : [], highlightClassName: styles.Highlight, - findChunks: (options) => this.updateMatches(options) + findChunks: this.updateMatches }; } - onClickDownloadResources(event, bundleId) { + onClickDownloadResources = (event) => { + const { bundleId } = this.props; this.props.downloadResources(bundleId); event.stopPropagation(); } - startSaveBundleTo(event) { + startSaveBundleTo = (event) => { const { bundlesSaveTo: savedToHistory, bundleId } = this.props; stopPropagation(event); const bundleSavedToInfo = getBundleExportInfo(bundleId, savedToHistory); @@ -125,7 +126,7 @@ class DBLEntryRow extends PureComponent { }); } - openInFolder(event) { + openInFolder = (event) => { const { bundlesSaveTo: savedToHistory, bundleId } = this.props; event.stopPropagation(); const bundleSavedToInfo = getBundleExportInfo(bundleId, savedToHistory); @@ -135,12 +136,12 @@ class DBLEntryRow extends PureComponent { } } - onOpenDBLEntryLink(event) { + onOpenDBLEntryLink = (event) => { const { dblId } = this.props; onOpenLink(event, `https://thedigitalbiblelibrary.org/entry?id=${dblId}`); } - onClickRemoveResources(event) { + onClickRemoveResources = (event) => { const { bundleId } = this.props; this.props.removeResources(bundleId); event.stopPropagation(); @@ -169,7 +170,7 @@ class DBLEntryRow extends PureComponent { >
- +
{ } } +DBLEntryRow.defaultProps = { + progress: null +}; + export default connect( mapStateToProps, mapDispatchToProps, From c684fc766c3adffafeaf03febe5a33d34c08cf60 Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Wed, 30 May 2018 22:26:24 -0500 Subject: [PATCH 11/13] fix "Open in Folder" --- app/components/DBLEntryRow.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/components/DBLEntryRow.js b/app/components/DBLEntryRow.js index 1ae8ede1ed..c110e57f5b 100644 --- a/app/components/DBLEntryRow.js +++ b/app/components/DBLEntryRow.js @@ -110,7 +110,8 @@ class DBLEntryRow extends PureComponent { } startSaveBundleTo = (event) => { - const { bundlesSaveTo: savedToHistory, bundleId } = this.props; + const { bundlesSaveTo, bundleId } = this.props; + const { savedToHistory } = bundlesSaveTo; stopPropagation(event); const bundleSavedToInfo = getBundleExportInfo(bundleId, savedToHistory); const defaultPath = bundleSavedToInfo ? bundleSavedToInfo.folderName : app.getPath('downloads'); @@ -127,7 +128,8 @@ class DBLEntryRow extends PureComponent { } openInFolder = (event) => { - const { bundlesSaveTo: savedToHistory, bundleId } = this.props; + const { bundlesSaveTo, bundleId } = this.props; + const { savedToHistory } = bundlesSaveTo; event.stopPropagation(); const bundleSavedToInfo = getBundleExportInfo(bundleId, savedToHistory); if (bundleSavedToInfo) { From d39e2cc0350fae29b47c9fa0eee6e9c9aaae86d1 Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Wed, 30 May 2018 22:36:29 -0500 Subject: [PATCH 12/13] fix Cleaned status/task behavior --- app/reducers/bundles.reducer.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/app/reducers/bundles.reducer.js b/app/reducers/bundles.reducer.js index 30387c9fe3..5dfac6f009 100644 --- a/app/reducers/bundles.reducer.js +++ b/app/reducers/bundles.reducer.js @@ -77,8 +77,14 @@ export function bundles(state = {}, action) { const resourcesRemoved = originalResourceRemoved.includes(resourceToRemove) ? resourcesRemoved : [...originalResourceRemoved, resourceToRemove]; const resourcesToRemove = bundle.resourcesToRemove || [...resourcesRemoved, 'unknown']; + const progress = calcProgress(resourcesRemoved.length, resourcesToRemove.length); + const hasCompletedRemovingResources = progress === 100; + const task = hasCompletedRemovingResources ? 'DOWNLOAD' : bundle.task; + const status = hasCompletedRemovingResources ? 'NOT_STARTED' : bundle.status; return { - progress: calcProgress(resourcesRemoved.length, resourcesToRemove.length), + task, + status, + progress, resourcesRemoved, resourcesToRemove }; @@ -86,15 +92,7 @@ export function bundles(state = {}, action) { } case bundleConstants.UPDATE_STATUS: { const progress = action.status === 'COMPLETED' ? 100 : null; - return updateTaskStatusProgress(action.id, null, action.status, progress, (bundle) => { - const hasCompletedRemovingResources = action.status === 'COMPLETED' && bundle.task === 'REMOVE_RESOURCES'; - const task = hasCompletedRemovingResources ? 'DOWNLOAD' : bundle.task; - const status = hasCompletedRemovingResources ? 'NOT_STARTED' : bundle.status; - return { - task, - status - }; - }); + return updateTaskStatusProgress(action.id, null, action.status, progress); } case bundleConstants.TOGGLE_MODE_PAUSE_RESUME: { const updatedItems = forkArray( From 91a58e94deabea77083a92f4ac1ce6279053c70f Mon Sep 17 00:00:00 2001 From: Eric Pyle Date: Wed, 30 May 2018 22:57:43 -0500 Subject: [PATCH 13/13] ### Version 0.6.0 #### Features - Add new entry rows after DBL dot Local discovers and downloads their metadata #### Fixes - Improved Search input performance - Improved scroll bar performance when loading new entries - Improved entry row selection (toggling tray menu) - Filter out rows that are missing name (possibly loading_metadata) --- CHANGELOG.md | 11 +++++++++++ app/package-lock.json | 2 +- app/package.json | 2 +- package-lock.json | 2 +- package.json | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55372ea850..5eb7b8121b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +### Version 0.6.0 + +#### Features +- Add new entry rows after DBL dot Local discovers and downloads their metadata + +#### Fixes +- Improved Search input performance +- Improved scroll bar performance when loading new entries +- Improved entry row selection (toggling tray menu) +- Filter out rows that are missing name (possibly loading_metadata) + ### Version 0.5.0 #### Features diff --git a/app/package-lock.json b/app/package-lock.json index 490c08b839..a12e62f97e 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,5 +1,5 @@ { "name": "nathanael", - "version": "0.5.0", + "version": "0.6.0", "lockfileVersion": 1 } diff --git a/app/package.json b/app/package.json index 8626ca15bf..0a47866d21 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "nathanael", "productName": "Nathanael", - "version": "0.5.0", + "version": "0.6.0", "description": "Electron frontend to DBL dot Local", "main": "./main.prod.js", "author": { diff --git a/package-lock.json b/package-lock.json index aeb46c0e08..c2e444695e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nathanael", - "version": "0.5.0", + "version": "0.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 876c3689c1..12e66e8c4a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nathanael", "productName": "Nathanael", - "version": "0.5.0", + "version": "0.6.0", "description": "Electron frontend to DBL dot Local", "scripts": { "build": "concurrently \"npm run build-main\" \"npm run build-renderer\"",