aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/js/ducks/utils
diff options
context:
space:
mode:
Diffstat (limited to 'web/src/js/ducks/utils')
-rw-r--r--web/src/js/ducks/utils/list.js93
-rw-r--r--web/src/js/ducks/utils/store.js194
-rwxr-xr-xweb/src/js/ducks/utils/view.js189
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
-}