aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/js/ducks/utils/view.js
blob: 2d23a39cabc49525e975141cf4b479b0d8216229 (plain)
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
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
}

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
}

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
}

export function sortedIndexOf(list, value, sortFn) {
    if (!sortFn) {
        sortFn = x => 0 // This triggers the linear search for flows that have the same sort value.
    }

    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
        }
    }

    // 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;
}

// 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)
            }
            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 currentView
        case REMOVE:
            let isInView_ = filterFn(currentList.byId[action.item.id])
            if (isInView_) {
                return sortedRemove(currentView, sortFn, action.item)
            }
            return currentView
        default:
            console.error("Unknown list action: ", action)
            return currentView
    }
}

export function updateViewFilter(list, filterFn = defaultFilterFn, sortFn = defaultSortFn) {
    let filtered = list.list.filter(filterFn)
    if (sortFn){
        filtered.sort(makeCompareFn(sortFn))
    }
    filtered.indexOf = x => sortedIndexOf(filtered, x, sortFn)

    return filtered
}

export function updateViewSort(list, sortFn = defaultSortFn) {
    let sorted = list.slice(0)
    if (sortFn) {
        sorted.sort(makeCompareFn(sortFn))
    }
    sorted.indexOf = x => sortedIndexOf(sorted, x, sortFn)

    return sorted
}