Repository navigation
Expand file tree
/
Copy pathgetContextsSortedAndRanked.ts
More file actions
72 lines (66 loc) · 3 KB
/
Copy pathgetContextsSortedAndRanked.ts
File metadata and controls
72 lines (66 loc) · 3 KB
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
import _ from 'lodash'
import State from '../@types/State'
import Thought from '../@types/Thought'
import ThoughtId from '../@types/ThoughtId'
import getContexts from '../selectors/getContexts'
import getThoughtById from '../selectors/getThoughtById'
import isRoot from '../util/isRoot'
import isVisibleContext from '../util/isVisibleContext'
import never from '../util/never'
import nonNull from '../util/nonNull'
import parentOf from '../util/parentOf'
import unroot from '../util/unroot'
import childIdsToThoughts from './childIdsToThoughts'
import rootedParentOf from './rootedParentOf'
import thoughtToPath from './thoughtToPath'
// sort missing thoughts to end
const MISSING_TOKEN = `${String.fromCharCode(Number.MAX_SAFE_INTEGER)}__MISSING__`
const PENDING_TOKEN = '__PENDING__'
/** Gets all contexts that the given thought is in, sorted and ranked. */
const getContextsSortedAndRanked = (state: State, value: string): Thought[] => {
const contexts = getContexts(state, value)
.filter(id => isVisibleContext(state, id))
.map((cxid, i) => {
const thought = getThoughtById(state, cxid)
const thoughtRanked: Thought = {
// if the context is pending, return a pending placeholder
...(thought || {
id: cxid,
childrenMap: {},
parentId: '' as ThoughtId, // ???
pending: true,
rank: i, // overwritten by contextSorted
value: PENDING_TOKEN,
lastUpdated: never(),
updatedBy: '',
created: never(),
}),
}
return thoughtRanked
})
/** Calculates a lexically sortable hash from a thought. */
const lexicalHash = (thought: Thought) => {
if (!thought || thought.value === PENDING_TOKEN) return MISSING_TOKEN
const simplePath = thoughtToPath(state, thought.id)
if (simplePath.length > 1 && isRoot([simplePath[0]])) return MISSING_TOKEN
const path = unroot(simplePath)
const breadcrumbs = rootedParentOf(state, parentOf(path))
const parent = getThoughtById(state, thought.parentId)
// Root token values are provider implementation details. Treat the root as an empty breadcrumb so
// context order does not change between providers that use different root ids.
const breadcrumbThoughts = isRoot(breadcrumbs) ? [] : childIdsToThoughts(state, breadcrumbs)
if (!breadcrumbThoughts.every(nonNull)) return MISSING_TOKEN
const encodedBreadcrumbs = breadcrumbThoughts
.map(thought => (thought ? thought.value : MISSING_TOKEN))
.join('\x00SEP2')
// for contexts that have the same ancestors, we need to sort by the rendered value (parent.value)
// use SEP1 to ensure the value is sorted ahead of the breadcrumbs separator SEP2
return [encodedBreadcrumbs, parent?.value || ''].join('\x00SEP1')
}
// sort by hashed ancestor values
const contextsSorted: Thought[] = _.sortBy(contexts, lexicalHash)
// generate dynamic ranks in sort order
.map((thought, i) => ({ ...thought, rank: i }))
return contextsSorted
}
export default getContextsSortedAndRanked