library.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import Vue from 'vue'
  2. import router from '@/router'
  3. import api from '@/api'
  4. import {
  5. parseNodesQueryParams,
  6. getUniqueNodesIds,
  7. getRelatedNodesIds,
  8. getRandomIds,
  9. parseTree,
  10. buildTree
  11. } from '@/store/utils.js'
  12. export default {
  13. state: {
  14. trees: {},
  15. randomNodes: undefined,
  16. nodebook: [],
  17. // LibraryOptions options
  18. tagsOptions: [],
  19. // LibraryOptions state
  20. mode: undefined,
  21. nodeDepartId: undefined,
  22. search: '',
  23. tags: [],
  24. strangeness: undefined
  25. },
  26. mutations: {
  27. // ╷ ╶┬╴┌─╮┌─╮╭─┐┌─╮╷ ╷
  28. // │ │ │╶┤├┬╯├─┤├┬╯╰─┤
  29. // ╰─╴╶┴╴└─╯╵ ╰╵ ╵╵ ╰╶─╯
  30. 'SET_NODEBOOK' (state, nodes) {
  31. state.nodebook = nodes
  32. },
  33. 'ADD_NODEBOOK_NODE' (state, [stack, parentId, childId]) {
  34. if (stack === undefined) {
  35. stack = childId === undefined ? [parentId] : [parentId, childId]
  36. state.nodebook.push(stack)
  37. return
  38. }
  39. if (stack !== undefined && parentId !== undefined) {
  40. state.nodebook.push(state.nodebook.splice(state.nodebook.indexOf(stack), 1)[0])
  41. }
  42. if (childId) {
  43. if (stack.includes(childId)) {
  44. stack.push(stack.splice(stack.indexOf(childId), 1)[0])
  45. } else {
  46. stack.push(childId)
  47. }
  48. }
  49. },
  50. 'REMOVE_NODEBOOK_NODE' (state, [stack, nodeId]) {
  51. if (stack[0] === nodeId) {
  52. state.nodebook.splice(state.nodebook.indexOf(stack), 1)
  53. } else {
  54. stack.splice(stack.indexOf(nodeId), 1)
  55. }
  56. },
  57. 'ADD_NODE_TREE' (state, [id, tree]) {
  58. Vue.set(state.trees, id, tree)
  59. },
  60. // ╭─╮┌─╮╶┬╴╶┬╴╭─╮╭╮╷╭─╴
  61. // │ │├─╯ │ │ │ ││││╰─╮
  62. // ╰─╯╵ ╵ ╶┴╴╰─╯╵╰╯╶─╯
  63. 'SET_TAGS_OPTIONS' (state, nodes) {
  64. const tags = nodes.reduce((tags, node) => tags.concat(node.tags || []), [])
  65. state.tagsOptions = Array.from(new Set(tags.map(tag => tag.name)))
  66. },
  67. 'UPDATE_TAGS' (state, tags) {
  68. state.tags = tags
  69. },
  70. 'UPDATE_STRANGENESS' (state, strangenessLvl) {
  71. state.strangeness = strangenessLvl
  72. },
  73. 'SET_MODE' (state, mode) {
  74. state.mode = mode
  75. },
  76. 'SET_NODE_DEPART_ID' (state, id) {
  77. state.nodeDepartId = id
  78. },
  79. 'SET_RANDOM_NODES' (state, ids) {
  80. state.randomNodes = ids
  81. },
  82. 'SET_SEARCH' (state, str) {
  83. state.search = str
  84. }
  85. },
  86. actions: {
  87. // ╷ ╶┬╴┌─╮┌─╮╭─┐┌─╮╷ ╷
  88. // │ │ │╶┤├┬╯├─┤├┬╯╰─┤
  89. // ╰─╴╶┴╴└─╯╵ ╰╵ ╵╵ ╰╶─╯
  90. async 'INIT_LIBRARY' ({ state, commit, dispatch, rootState }, { dataLevel = 'initial' } = {}) {
  91. await dispatch('GET_ALL_NODES_IDS', { variant: 'depart', dataLevel })
  92. if (state.nodebook && state.nodebook.length) {
  93. commit('UPDATE_OPTIONS_VISIBILITY', false)
  94. }
  95. return rootState.ids.depart
  96. },
  97. async 'INIT_LIBRARY_TREE' ({ state, commit, dispatch, rootState }) {
  98. const ids = await dispatch('INIT_LIBRARY')
  99. // set Katalin Molnár "Quant à je" (id: 8) as default selected node
  100. return dispatch('SET_NODE_DEPART_ID', 8)
  101. },
  102. async 'INIT_LIBRARY_MAP' ({ state, commit, dispatch }) {
  103. await dispatch('INIT_LIBRARY')
  104. return dispatch('ROLL_RANDOM_NODES')
  105. },
  106. async 'INIT_LIBRARY_LIST' ({ state, commit, dispatch }) {
  107. const departIds = await dispatch('INIT_LIBRARY', { dataLevel: 'partial' })
  108. const nodes = await dispatch('GET_NODES', { ids: departIds, dataLevel: 'partial' })
  109. commit('SET_TAGS_OPTIONS', nodes)
  110. return nodes
  111. },
  112. async 'UPDATE_NODEBOOK' ({ state, commit, dispatch }, query) {
  113. const { mode = 'tree', nodebook } = parseNodesQueryParams(query)
  114. commit('SET_MODE', mode)
  115. const ids = [].concat(...nodebook)
  116. await dispatch('GET_NODES', { ids, dataLevel: 'full' })
  117. commit('SET_NODEBOOK', nodebook)
  118. commit('ADD_HISTORY_ENTRIES', ids)
  119. },
  120. // ╭╮╷╭─╮┌─╮┌─╴╭─╴
  121. // ││││ ││ │├─╴╰─╮
  122. // ╵╰╯╰─╯└─╯╰─╴╶─╯
  123. 'UPDATE_QUERY_NODES' ({ state }) {
  124. const query = {
  125. mode: state.mode,
  126. nodes: [...state.nodebook.map(ids => ids.join(','))]
  127. }
  128. if (router.currentRoute.name !== 'library') {
  129. router.push({ name: 'library', query })
  130. } else {
  131. router.push({ query })
  132. }
  133. },
  134. async 'OPEN_NODE' ({ state, commit, dispatch }, { parentId, childId }) {
  135. if (state.nodebook === undefined || !state.nodebook.length) {
  136. commit('UPDATE_OPTIONS_VISIBILITY', false)
  137. }
  138. const stack = state.nodebook.find(stack => stack[0] === parentId)
  139. commit('ADD_NODEBOOK_NODE', [stack, parentId, childId])
  140. dispatch('UPDATE_QUERY_NODES')
  141. commit('ADD_HISTORY_ENTRIES', [childId || parentId])
  142. },
  143. 'CLOSE_NODE' ({ state, commit, dispatch }, { parentId, childId }) {
  144. const stack = state.nodebook.find(stack => stack.includes(parentId) && (childId ? stack.includes(childId) : true))
  145. commit('REMOVE_NODEBOOK_NODE', [stack, childId || parentId])
  146. if (!state.nodebook.length) {
  147. commit('UPDATE_OPTIONS_VISIBILITY', true)
  148. }
  149. dispatch('UPDATE_QUERY_NODES')
  150. },
  151. // ╭─╮┌─╮╶┬╴╶┬╴╭─╮╭╮╷╭─╴
  152. // │ │├─╯ │ │ │ ││││╰─╮
  153. // ╰─╯╵ ╵ ╶┴╴╰─╯╵╰╯╶─╯
  154. async 'SET_NODE_DEPART_ID' ({ state, rootState, commit, dispatch, getters }, id) {
  155. if (state.nodeDepartId === id) return
  156. commit('SET_NODE_DEPART_ID', id)
  157. if (!(id in state.trees)) {
  158. const treeData = parseTree(await api.queryRecursiveNodes([id]))
  159. const nodes = await dispatch('GET_NODES', { ids: getUniqueNodesIds(treeData), dataLevel: 'initial' })
  160. commit('ADD_NODE_TREE', [id, buildTree(treeData, nodes)])
  161. }
  162. return getters.nodeTree
  163. },
  164. async 'ROLL_RANDOM_NODES' ({ state, rootState, commit, dispatch }) {
  165. commit('SET_RANDOM_NODES', undefined)
  166. const ids = getRandomIds(rootState.ids.depart)
  167. const departNodes = await dispatch('GET_NODES', { ids, dataLevel: 'partial' })
  168. const relatedIds = getRelatedNodesIds(departNodes, ids)
  169. const relatedNodes = await dispatch('GET_NODES', { ids: relatedIds, dataLevel: 'partial' })
  170. const nodes = [...departNodes, ...relatedNodes]
  171. commit('SET_RANDOM_NODES', [...ids, ...relatedIds])
  172. commit('SET_TAGS_OPTIONS', nodes)
  173. },
  174. 'UPDATE_QUERY_MODE' (store, mode) {
  175. if (router.currentRoute.query.mode === mode) return
  176. router.push({ query: { mode } })
  177. }
  178. },
  179. getters: {
  180. // LibraryOptions state
  181. mode: state => state.mode,
  182. nodeDepartId: state => state.nodeDepartId,
  183. search: state => state.search,
  184. tags: state => state.tags,
  185. strangeness: state => state.strangeness,
  186. // LibraryOptions options
  187. tagsOptions: state => state.tagsOptions,
  188. nodesDeparts: (state, getters, rootState) => {
  189. const departIds = rootState.ids.depart
  190. if (departIds === undefined) return
  191. const nodes = departIds.map(id => rootState.nodes[id])
  192. if (nodes.some(node => node === undefined)) return
  193. return nodes
  194. },
  195. nodesDepartsOptions: (state, getters, rootState) => {
  196. const nodes = getters.nodesDeparts
  197. if (nodes === undefined) return
  198. return nodes.sort((a, b) => {
  199. const prev = a.authors[0].last_name.toLowerCase()
  200. const next = b.authors[0].last_name.toLowerCase()
  201. if (prev === next) return 0
  202. return prev < next ? -1 : 1
  203. })
  204. },
  205. // Library
  206. nodebook: (state, getters, rootState) => {
  207. return state.nodebook.map(([parentId, ...childrenIds]) => {
  208. return {
  209. parent: rootState.nodes[parentId],
  210. children: childrenIds.map(id => rootState.nodes[id])
  211. }
  212. })
  213. },
  214. // LibraryList
  215. orderedTextsDepart: (state, getters, rootState) => {
  216. const departIds = rootState.ids.depart
  217. if (departIds === undefined || rootState.nodes[departIds[0]] === undefined) return
  218. const nodesDict = {}
  219. for (const node of rootState.ids.depart.map(id => rootState.nodes[id])) {
  220. if (!node.authors) {
  221. if (!('~' in nodesDict)) nodesDict['~'] = []
  222. nodesDict['~'].push(node)
  223. } else {
  224. node.authors.forEach((author, i) => {
  225. const firstChar = author.last_name[0].toUpperCase()
  226. if (!(firstChar in nodesDict)) nodesDict[firstChar] = []
  227. nodesDict[firstChar].push({ last_name: author.last_name, node })
  228. })
  229. }
  230. }
  231. return Object.entries(nodesDict).sort((a, b) => a[0] < b[0] ? -1 : 1).map(group => {
  232. if (group[0] === '~') return group
  233. return [group[0], group[1].sort((a, b) => {
  234. const prev = a.last_name.toLowerCase()
  235. const next = b.last_name.toLowerCase()
  236. return prev.localeCompare(next)
  237. }).map(node => node.node)]
  238. })
  239. },
  240. // LibraryMap
  241. randomNodes: (state, getters, rootState) => {
  242. if (state.randomNodes === undefined) return
  243. return state.randomNodes.map(id => rootState.nodes[id])
  244. },
  245. // LibraryTree
  246. nodeTree: state => {
  247. return state.trees[state.nodeDepartId] || { nodes: [], links: [] }
  248. },
  249. // Commons
  250. activeNodes: state => {
  251. if (!state.nodebook) return []
  252. return state.nodebook.reduce((acc, ids) => [...acc, ...ids], [])
  253. }
  254. }
  255. }