diff options
Diffstat (limited to 'web/src/js/ducks/utils')
-rw-r--r-- | web/src/js/ducks/utils/list.js | 93 | ||||
-rw-r--r-- | web/src/js/ducks/utils/store.js | 194 | ||||
-rwxr-xr-x | web/src/js/ducks/utils/view.js | 189 |
3 files changed, 194 insertions, 282 deletions
diff --git a/web/src/js/ducks/utils/list.js b/web/src/js/ducks/utils/list.js deleted file mode 100644 index 339fb921..00000000 --- a/web/src/js/ducks/utils/list.js +++ /dev/null @@ -1,93 +0,0 @@ -import _ from 'lodash' - -export const ADD = 'LIST_ADD' -export const UPDATE = 'LIST_UPDATE' -export const REMOVE = 'LIST_REMOVE' -export const RECEIVE = 'LIST_RECEIVE' - -const defaultState = { - data: [], - byId: {}, - indexOf: {}, -} - -export default function reduce(state = defaultState, action) { - switch (action.type) { - - case ADD: - return { - ...state, - data: [...state.data, action.item], - byId: { ...state.byId, [action.item.id]: action.item }, - indexOf: { ...state.indexOf, [action.item.id]: state.data.length }, - } - - case UPDATE: { - const index = state.indexOf[action.item.id] - - if (index == null) { - return state - } - - const data = [...state.data] - - data[index] = action.item - - return { - ...state, - data, - byId: { ...state.byId, [action.item.id]: action.item } - } - } - - case REMOVE: { - const index = state.indexOf[action.id] - - if (index == null) { - return state - } - - const data = [...state.data] - const indexOf = { ...state.indexOf, [action.id]: null } - - data.splice(index, 1) - for (let i = data.length - 1; i >= index; i--) { - indexOf[data[i].id] = i - } - - return { - ...state, - data, - indexOf, - byId: { ...state.byId, [action.id]: null }, - } - } - - case RECEIVE: - return { - ...state, - data: action.list, - byId: _.fromPairs(action.list.map(item => [item.id, item])), - indexOf: _.fromPairs(action.list.map((item, index) => [item.id, index])), - } - - default: - return state - } -} - -export function add(item) { - return { type: ADD, item } -} - -export function update(item) { - return { type: UPDATE, item } -} - -export function remove(id) { - return { type: REMOVE, id } -} - -export function receive(list) { - return { type: RECEIVE, list } -} diff --git a/web/src/js/ducks/utils/store.js b/web/src/js/ducks/utils/store.js new file mode 100644 index 00000000..3252d697 --- /dev/null +++ b/web/src/js/ducks/utils/store.js @@ -0,0 +1,194 @@ +export const SET_FILTER = 'LIST_SET_FILTER' +export const SET_SORT = 'LIST_SET_SORT' +export const ADD = 'LIST_ADD' +export const UPDATE = 'LIST_UPDATE' +export const REMOVE = 'LIST_REMOVE' +export const RECEIVE = 'LIST_RECEIVE' + +const defaultState = { + byId: {}, + list: [], + listIndex: {}, + view: [], + viewIndex: {}, +} + +export default function reduce(state = defaultState, action) { + + let { byId, list, listIndex, view, viewIndex } = state + + switch (action.type) { + case SET_FILTER: + view = list.filter(action.filter).sort(action.sort) + viewIndex = {} + view.forEach((item, index) => { + viewIndex[item.id] = index + }) + break + + case SET_SORT: + view = [...view].sort(action.sort) + viewIndex = {} + view.forEach((item, index) => { + viewIndex[item.id] = index + }) + break + + case ADD: + if (action.item.id in byId) { + // we already had that. + break + } + byId = { ...byId, [action.item.id]: action.item } + listIndex = { ...listIndex, [action.item.id]: list.length } + list = [...list, action.item] + if (action.filter(action.item)) { + ({ view, viewIndex } = sortedInsert(state, action.item, action.sort)) + } + break + + case UPDATE: + byId = { ...byId, [action.item.id]: action.item } + list = [...list] + list[listIndex[action.item.id]] = action.item + + let hasOldItem = action.item.id in viewIndex + let hasNewItem = action.filter(action.item) + if (hasNewItem && !hasOldItem) { + ({view, viewIndex} = sortedInsert(state, action.item, action.sort)) + } + else if (!hasNewItem && hasOldItem) { + ({data: view, dataIndex: viewIndex} = removeData(view, viewIndex, action.item.id)) + } + else if (hasNewItem && hasOldItem) { + ({view, viewIndex} = sortedUpdate(state, action.item, action.sort)) + } + break + + case REMOVE: + if (!(action.id in byId)) { + break + } + delete byId[action.id]; + ({data: list, dataIndex: listIndex} = removeData(list, listIndex, action.id)) + + if (action.id in viewIndex) { + ({data: view, dataIndex: viewIndex} = removeData(view, viewIndex, action.id)) + } + break + + case RECEIVE: + list = action.list + listIndex = {} + byId = {} + list.forEach((item, i) => { + byId[item.id] = item + listIndex[item.id] = i + }) + view = list.filter(action.filter).sort(action.sort) + viewIndex = {} + view.forEach((item, index) => { + viewIndex[item.id] = index + }) + break + } + return { byId, list, listIndex, view, viewIndex } +} + + +export function setFilter(filter = defaultFilter, sort = defaultSort) { + return { type: SET_FILTER, filter, sort } +} + +export function setSort(sort = defaultSort) { + return { type: SET_SORT, sort } +} + +export function add(item, filter = defaultFilter, sort = defaultSort) { + return { type: ADD, item, filter, sort } +} + +export function update(item, filter = defaultFilter, sort = defaultSort) { + return { type: UPDATE, item, filter, sort } +} + +export function remove(id) { + return { type: REMOVE, id } +} + +export function receive(list, filter = defaultFilter, sort = defaultSort) { + return { type: RECEIVE, list, filter, sort } +} + +function sortedInsert(state, item, sort) { + const index = sortedIndex(state.view, item, sort) + const view = [...state.view] + const viewIndex = { ...state.viewIndex } + + view.splice(index, 0, item) + for (let i = view.length - 1; i >= index; i--) { + viewIndex[view[i].id] = i + } + + return { view, viewIndex } +} + +function removeData(currentData, currentDataIndex, id) { + const index = currentDataIndex[id] + const data = [...currentData] + const dataIndex = { ...currentDataIndex } + delete dataIndex[id]; + + data.splice(index, 1) + for (let i = data.length - 1; i >= index; i--) { + dataIndex[data[i].id] = i + } + + return { data, dataIndex } +} + +function sortedUpdate(state, item, sort) { + let view = [...state.view] + let viewIndex = { ...state.viewIndex } + let index = viewIndex[item.id] + view[index] = item + while (index + 1 < view.length && sort(view[index], view[index + 1]) > 0) { + view[index] = view[index + 1] + view[index + 1] = item + viewIndex[item.id] = index + 1 + viewIndex[view[index].id] = index + ++index + } + while (index > 0 && sort(view[index], view[index - 1]) < 0) { + view[index] = view[index - 1] + view[index - 1] = item + viewIndex[item.id] = index - 1 + viewIndex[view[index].id] = index + --index + } + return { view, viewIndex } +} + +function sortedIndex(list, item, sort) { + let low = 0 + let high = list.length + + while (low < high) { + const middle = (low + high) >>> 1 + if (sort(item, list[middle]) >= 0) { + low = middle + 1 + } else { + high = middle + } + } + + return low +} + +function defaultFilter() { + return true +} + +function defaultSort(a, b) { + return 0 +} diff --git a/web/src/js/ducks/utils/view.js b/web/src/js/ducks/utils/view.js deleted file mode 100755 index 6bf0a63e..00000000 --- a/web/src/js/ducks/utils/view.js +++ /dev/null @@ -1,189 +0,0 @@ -import _ from 'lodash' - -export const UPDATE_FILTER = 'VIEW_UPDATE_FILTER' -export const UPDATE_SORT = 'VIEW_UPDATE_SORT' -export const ADD = 'VIEW_ADD' -export const UPDATE = 'VIEW_UPDATE' -export const REMOVE = 'VIEW_REMOVE' -export const RECEIVE = 'VIEW_RECEIVE' - -const defaultState = { - data: [], - indexOf: {}, -} - -export default function reduce(state = defaultState, action) { - switch (action.type) { - - case UPDATE_FILTER: - { - const data = action.list.filter(action.filter).sort(action.sort) - return { - ...state, - data, - indexOf: _.fromPairs(data.map((item, index) => [item.id, index])), - } - } - - case UPDATE_SORT: - { - const data = [...state.data].sort(action.sort) - return { - ...state, - data, - indexOf: _.fromPairs(data.map((item, index) => [item.id, index])), - } - } - - case ADD: - if (state.indexOf[action.item.id] != null || !action.filter(action.item)) { - return state - } - return { - ...state, - ...sortedInsert(state, action.item, action.sort), - } - - case REMOVE: - if (state.indexOf[action.id] == null) { - return state - } - return { - ...state, - ...sortedRemove(state, action.id), - } - - case UPDATE: - let hasOldItem = state.indexOf[action.item.id] !== null && state.indexOf[action.item.id] !== undefined - let hasNewItem = action.filter(action.item) - if (!hasNewItem && !hasOldItem) { - return state - } - if (hasNewItem && !hasOldItem) { - return { - ...state, - ...sortedInsert(state, action.item, action.sort) - } - } - if (!hasNewItem && hasOldItem) { - return { - ...state, - ...sortedRemove(state, action.item.id) - } - } - if (hasNewItem && hasOldItem) { - return { - ...state, - ...sortedUpdate(state, action.item, action.sort), - } - } - case RECEIVE: - { - const data = action.list.filter(action.filter).sort(action.sort) - return { - ...state, - data, - indexOf: _.fromPairs(data.map((item, index) => [item.id, index])), - } - } - - default: - return state - } -} - -export function updateFilter(list, filter = defaultFilter, sort = defaultSort) { - return { type: UPDATE_FILTER, list, filter, sort } -} - -export function updateSort(sort = defaultSort) { - return { type: UPDATE_SORT, sort } -} - -export function add(item, filter = defaultFilter, sort = defaultSort) { - return { type: ADD, item, filter, sort } -} - -export function update(item, filter = defaultFilter, sort = defaultSort) { - return { type: UPDATE, item, filter, sort } -} - -export function remove(id) { - return { type: REMOVE, id } -} - -export function receive(list, filter = defaultFilter, sort = defaultSort) { - return { type: RECEIVE, list, filter, sort } -} - -function sortedInsert(state, item, sort) { - const index = sortedIndex(state.data, item, sort) - 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 - } - - return { data, indexOf } -} - -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 - } - - return { data, indexOf } -} - -function sortedUpdate(state, item, sort) { - let data = [ ...state.data ] - let indexOf = { ...state.indexOf } - let index = indexOf[item.id] - data[index] = item - while (index + 1 < data.length && sort(data[index], data[index + 1]) > 0) { - data[index] = data[index + 1] - data[index + 1] = item - indexOf[item.id] = index + 1 - indexOf[data[index].id] = index - ++index - } - while (index > 0 && sort(data[index], data[index - 1]) < 0) { - data[index] = data[index - 1] - data[index - 1] = item - indexOf[item.id] = index - 1 - indexOf[data[index].id] = index - --index - } - return { data, indexOf } -} - -function sortedIndex(list, item, sort) { - let low = 0 - let high = list.length - - while (low < high) { - const middle = (low + high) >>> 1 - if (sort(item, list[middle]) >= 0) { - low = middle + 1 - } else { - high = middle - } - } - - return low -} - -function defaultFilter() { - return true -} - -function defaultSort(a, b) { - return 0 -} |