forked from WorldBrain/Memex
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathselectors.js
229 lines (204 loc) · 7.18 KB
/
selectors.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import { createSelector } from 'reselect'
import pickBy from 'lodash/fp/pickBy'
import moment from 'moment'
import {
FILTERS,
STATUS,
DOC_TIME_EST,
IMPORT_TYPE as TYPE,
IMPORT_TYPE_DISPLAY as TYPE_DISPLAY,
DOWNLOAD_STATUS as DL_STAT,
} from './constants'
export const imports = state => state.imports
export const importStatus = createSelector(imports, state => state.importStatus)
export const downloadData = createSelector(imports, state => state.downloadData)
export const downloadDataFilter = createSelector(
imports,
state => state.downloadDataFilter,
)
export const fail = createSelector(imports, state => state.fail)
export const success = createSelector(imports, state => state.success)
export const totals = createSelector(imports, state => state.totals)
const completed = createSelector(imports, state => state.completed)
export const allowTypes = createSelector(imports, state => state.allowTypes)
export const loadingMsg = createSelector(imports, state => state.loadingMsg)
export const blobUrl = createSelector(imports, state => state.blobUrl)
/**
* Currently only used for analytics; derive the import type from `allowTypes` state
*/
export const allowTypesString = createSelector(allowTypes, state => {
const val = []
for (const type in state) {
if (state[type]) {
val.push(TYPE_DISPLAY[type])
}
}
return val.join('+')
})
// TODO: Mocked out for now due to UI performance issues with show details views
export const showDownloadDetails = () => false
// export const showDownloadDetails = createSelector(
// imports,
// state => state.showDownloadDetails,
// )
// Adv settings mode
export const concurrency = createSelector(imports, state => state.concurrency)
export const processErrors = createSelector(
imports,
state => state.processErrors,
)
export const bookmarkImports = createSelector(
imports,
state => state.bookmarkImports,
)
export const indexTitle = createSelector(imports, state => state.indexTitle)
const getImportStatusFlag = status =>
createSelector(importStatus, importStatus => importStatus === status)
// Main import state selectors
export const isLoading = getImportStatusFlag(STATUS.LOADING)
export const isIdle = getImportStatusFlag(STATUS.IDLE)
export const isRunning = getImportStatusFlag(STATUS.RUNNING)
export const isPaused = getImportStatusFlag(STATUS.PAUSED)
export const isStopped = getImportStatusFlag(STATUS.STOPPED)
export const shouldRenderEsts = createSelector(
isIdle,
isLoading,
(idle, loading) => idle || loading,
)
export const shouldRenderProgress = createSelector(
isRunning,
isPaused,
(running, paused) => running || paused,
)
/**
* Derives ready-to-use download details data (rendered into rows).
* Performs a filter depending on the current projection selected in UI,
* as well as map from store data to UI data.
*/
export const downloadDetailsData = createSelector(
downloadData,
downloadDataFilter,
(downloadData, filter) => {
if (filter !== FILTERS.ALL) {
downloadData = downloadData.filter(({ status }) => {
switch (filter) {
case FILTERS.SUCC:
return status !== DL_STAT.FAIL
case FILTERS.FAIL:
return status === DL_STAT.FAIL
default:
return true
}
})
}
return downloadData.map(({ url, status, error }) => ({
url,
downloaded: status,
error,
}))
},
)
const getProgress = (success, fail, total, allow) => ({
total: allow ? total : 0,
success,
fail,
complete: success + fail,
})
/**
* Derives progress state from completed + total state counts.
*/
export const progress = createSelector(
success,
fail,
totals,
allowTypes,
(...args) => ({
[TYPE.HISTORY]: getProgress(...args.map(arg => arg[TYPE.HISTORY])),
[TYPE.BOOKMARK]: getProgress(...args.map(arg => arg[TYPE.BOOKMARK])),
[TYPE.OTHERS]: getProgress(...args.map(arg => arg[TYPE.OTHERS])),
}),
)
export const successCount = createSelector(
progress,
allowTypes,
(progress, allowTypes) =>
(allowTypes.h ? progress[TYPE.HISTORY].success : 0) +
(allowTypes.b ? progress[TYPE.BOOKMARK].success : 0) +
(allowTypes.o ? progress[TYPE.OTHERS].success : 0),
)
export const failCount = createSelector(
progress,
allowTypes,
(progress, allowTypes) =>
(allowTypes.h ? progress[TYPE.HISTORY].fail : 0) +
(allowTypes.b ? progress[TYPE.BOOKMARK].fail : 0) +
(allowTypes.o ? progress[TYPE.OTHERS].fail : 0),
)
export const progressPercent = createSelector(
progress,
allowTypes,
(progress, allowTypes) => {
const total =
(allowTypes[TYPE.HISTORY] ? progress[TYPE.HISTORY].total : 0) +
(allowTypes[TYPE.BOOKMARK] ? progress[TYPE.BOOKMARK].total : 0) +
(allowTypes[TYPE.OTHERS] ? progress[TYPE.OTHERS].total : 0)
const complete =
(allowTypes[TYPE.HISTORY] ? progress[TYPE.HISTORY].complete : 0) +
(allowTypes[TYPE.BOOKMARK] ? progress[TYPE.BOOKMARK].complete : 0) +
(allowTypes[TYPE.OTHERS] ? progress[TYPE.OTHERS].complete : 0)
return (complete / total) * 100
},
)
// Util formatting functions for download time estimates
const getHours = time => Math.floor(time / 3600).toFixed(0)
const getMins = time =>
Math.floor((time - getHours(time) * 3600) / 60).toFixed(0)
const getPaddedMins = time =>
getMins(time) < 10 ? `0${getMins(time)}` : getMins(time)
const getTimeEstStr = time => {
const timeEstimate = `${getHours(time)}:${getPaddedMins(time)}`
return timeEstimate === '0:00'
? timeEstimate
: moment.duration(timeEstimate).humanize()
}
const getEstimate = (complete, remaining) => ({
complete,
remaining,
timeRemaining: getTimeEstStr(remaining * DOC_TIME_EST),
})
/**
* Derives estimates state from completed + total state counts.
*/
export const estimates = createSelector(
completed,
totals,
(completed, totals) => ({
[TYPE.HISTORY]: getEstimate(
completed[TYPE.HISTORY],
totals[TYPE.HISTORY],
),
[TYPE.BOOKMARK]: getEstimate(
completed[TYPE.BOOKMARK],
totals[TYPE.BOOKMARK],
),
[TYPE.OTHERS]: getEstimate(completed[TYPE.OTHERS], totals[TYPE.OTHERS]),
}),
)
export const isStartBtnDisabled = createSelector(
allowTypes,
estimates,
(allowTypes, estimates) => {
const pickByAllowedTypes = pickBy(
(isAllowed, type) => isAllowed || isAllowed !== '',
)
// Map-reduce the remaining (allowed) estimates to disable button when remaining is 0
const noImportsRemaining = Object.keys(pickByAllowedTypes(allowTypes))
.map(importType => estimates[importType].remaining === 0)
.reduce((prev, curr) => prev && curr, true)
const allCheckboxesDisabled =
!allowTypes[TYPE.HISTORY] &&
!allowTypes[TYPE.BOOKMARK] &&
allowTypes[TYPE.OTHERS] === ''
return allCheckboxesDisabled || noImportsRemaining
},
)