diff options
Diffstat (limited to 'web/src/js/ducks/utils/view.js')
-rwxr-xr-x[-rw-r--r--] | web/src/js/ducks/utils/view.js | 243 |
1 files changed, 133 insertions, 110 deletions
diff --git a/web/src/js/ducks/utils/view.js b/web/src/js/ducks/utils/view.js index 01d57b17..3b552378 100644..100755 --- a/web/src/js/ducks/utils/view.js +++ b/web/src/js/ducks/utils/view.js @@ -1,134 +1,157 @@ -import {ADD, UPDATE, REMOVE, REQUEST_LIST, RECEIVE_LIST} from "./list" - -const defaultFilterFn = x => true -const defaultSortFn = false - -const makeCompareFn = sortFn => { - let compareFn = (a, b) => { - let akey = sortFn(a), - bkey = sortFn(b) - if (akey < bkey) { - return -1 - } else if (akey > bkey) { - return 1 - } else { - return 0 - } - } - // need to adjust sortedIndexOf as well - // if (sortFn.reverse) - // return (a, b) => compareFn(b, a) - return compareFn -} +import _ from 'lodash' -const sortedInsert = (list, sortFn, item) => { - let l = [...list, item] - l.indexOf = x => sortedIndexOf(l, x, sortFn) - let compareFn = makeCompareFn(sortFn) - - // only sort if sorting order is not correct yet - if (sortFn && compareFn(list[list.length - 1], item) > 0) { - // TODO: This is untested - console.debug("sorting view...") - l.sort(compareFn) - } - return l -} +export const UPDATE_FILTER = 'VIEW_UPDATE_FILTER' +export const UPDATE_SORTER = 'VIEW_UPDATE_SORTER' +export const ADD = 'VIEW_ADD' +export const UPDATE = 'VIEW_UPDATE' +export const REMOVE = 'VIEW_REMOVE' +export const RECEIVE = 'VIEW_RECEIVE' -const sortedRemove = (list, sortFn, item) => { - let itemId = item.id - let l = list.filter(x => x.id !== itemId) - l.indexOf = x => sortedIndexOf(l, x, sortFn) - return l +const defaultState = { + data: [], + indexOf: {}, } -export function sortedIndexOf(list, value, sortFn) { - if (!sortFn) { - sortFn = x => 0 // This triggers the linear search for flows that have the same sort value. - } +export default function reduce(state = defaultState, action) { + switch (action.type) { - let low = 0, - high = list.length, - val = sortFn(value), - mid; - while (low < high) { - mid = (low + high) >>> 1; - if (sortFn(list[mid]) < val) { - low = mid + 1 - } else { - high = mid + case UPDATE_FILTER: { + const data = _.values(action.list.data).filter(action.filter).sort(action.sorter) + return { + ...state, + data, + indexOf: _.fromPairs(data.map((item, index) => [item.id, index])), + } } - } - // Two flows may have the same sort value. - // we previously determined the leftmost flow with the same sort value, - // so no we need to scan linearly - while (list[low].id !== value.id && sortFn(list[low + 1]) === val) { - low++ - } - return low; -} + case UPDATE_SORTER: { + const data = [...state.data].sort(action.sorter) + return { + ...state, + data, + indexOf: _.fromPairs(data.map((item, index) => [item.id, index])) + } + } -// for when the list changes -export function updateViewList(currentView, currentList, nextList, action, filterFn = defaultFilterFn, sortFn = defaultSortFn) { - switch (action.cmd) { - case REQUEST_LIST: - return currentView - case RECEIVE_LIST: - return updateViewFilter(nextList, filterFn, sortFn) case ADD: - if (filterFn(action.item)) { - return sortedInsert(currentView, sortFn, action.item) + if (state.indexOf[action.item.id] != null || !action.filter(action.item)) { + return state } - return currentView - case UPDATE: - // let's determine if it's in the view currently and if it should be in the view. - let currentItemState = currentList.byId[action.item.id], - nextItemState = action.item, - isInView = filterFn(currentItemState), - shouldBeInView = filterFn(nextItemState) - - if (!isInView && shouldBeInView) - return sortedInsert(currentView, sortFn, action.item) - if (isInView && !shouldBeInView) - return sortedRemove(currentView, sortFn, action.item) - if (isInView && shouldBeInView) { - let s = [...currentView] - s.indexOf = x => sortedIndexOf(s, x, sortFn) - s[s.indexOf(currentItemState)] = nextItemState - if (sortFn && sortFn(currentItemState) !== sortFn(nextItemState)) - s.sort(makeCompareFn(sortFn)) - return s + return { + ...state, + ...sortedInsert(state, action.item, action.sorter), } - return currentView + case REMOVE: - let isInView_ = filterFn(currentList.byId[action.item.id]) - if (isInView_) { - return sortedRemove(currentView, sortFn, action.item) + if (state.indexOf[action.item.id] == null) { + return state + } + return { + ...state, + ...sortedRemove(state, action.id), + } + + case UPDATE: { + if (state.indexOf[action.item.id] == null) { + return + } + const nextState = { + ...state, + ...sortedRemove(state, action.id), } - return currentView + if (!action.filter(action.item)) { + return nextState + } + return { + ...nextState, + ...sortedInsert(nextState, action.item, action.sorter) + } + } + + case RECEIVE: { + const data = _.values(action.list.data).filter(action.filter).sort(action.sorter) + return { + ...state, + data, + indexOf: _.fromPairs(data.map((item, index) => [item.id, index])), + } + } + default: - console.error("Unknown list action: ", action) - return currentView + return state } } -export function updateViewFilter(list, filterFn = defaultFilterFn, sortFn = defaultSortFn) { - let filtered = list.list.filter(filterFn) - if (sortFn){ - filtered.sort(makeCompareFn(sortFn)) +export function updateFilter(list, filter = defaultFilter, sorter = defaultSorter) { + return { type: UPDATE_FILTER, list, filter, sorter } +} + +export function updateSorter(sorter = defaultSorter) { + return { type: UPDATE_SORTER, sorter } +} + +export function add(item, filter = defaultFilter, sorter = defaultSorter) { + return { type: ADD, item, filter, sorter } +} + +export function update(id, item, filter = defaultFilter, sorter = defaultSorter) { + return { type: UPDATE, id, item, filter, sorter } +} + +export function remove(id) { + return { type: REMOVE, id } +} + +export function receive(list, filter = defaultFilter, sorter = defaultSorter) { + return { type: RECEIVE, list, filter, sorter } +} + +function sortedInsert(state, item, sorter) { + const index = sortedIndex(state.data, item, sorter) + const data = [...state.data] + const indexOf = { ...state.indexOf } + + data.splice(index, 0, item) + for (let i = data.length - 1; i >= index; i--) { + indexOf[data[i].id] = i } - filtered.indexOf = x => sortedIndexOf(filtered, x, sortFn) - return filtered + return { data, indexOf } } -export function updateViewSort(list, sortFn = defaultSortFn) { - let sorted = [...list] - if (sortFn) { - sorted.sort(makeCompareFn(sortFn)) +function sortedRemove(state, id) { + const index = state.indexOf[id] + const data = [...state.data] + const indexOf = { ...state.indexOf, [id]: null } + + data.splice(index, 1) + for (let i = data.length - 1; i >= index; i--) { + indexOf[data[i].id] = i } - sorted.indexOf = x => sortedIndexOf(sorted, x, sortFn) - return sorted + return { data, indexOf } +} + +function sortedIndex(list, item, sorter) { + let low = 0 + let high = list.length + + while (low < high) { + const middle = (low + high) >>> 1 + if (sorter(item, list[middle]) > 0) { + low = middle + 1 + } else { + high = middle + } + } + + return low +} + +function defaultFilter() { + return true +} + +function defaultSorter(a, b) { + return 0 } |