Files: 741ac0a148c2a18f2bc5d54ad7582e7c82430d2b / modules / sheet / editTags.js
5134 bytesRaw
1 | const nest = require('depnest') |
2 | const { h, Value, Struct, map, computed } = require('mutant') |
3 | const MutantArray = require('mutant/Array') |
4 | const concat = require('lodash/concat') |
5 | const filter = require('lodash/filter') |
6 | const zip = require('lodash/zip') |
7 | const forEach = require('lodash/forEach') |
8 | const addSuggest = require('suggest-box') |
9 | |
10 | exports.gives = nest('sheet.editTags') |
11 | |
12 | exports.needs = nest({ |
13 | 'about.obs.valueFrom': 'first', |
14 | 'keys.sync.id': 'first', |
15 | 'sheet.display': 'first', |
16 | 'tag': { |
17 | 'async': { |
18 | 'apply': 'first', |
19 | 'create': 'first', |
20 | 'name': 'first' |
21 | }, |
22 | 'html': { |
23 | 'tag': 'first' |
24 | }, |
25 | 'obs': { |
26 | 'messageTags': 'first', |
27 | 'allTags': 'first' |
28 | } |
29 | } |
30 | }) |
31 | |
32 | exports.create = function(api) { |
33 | return nest({ 'sheet.editTags': editTags }) |
34 | |
35 | function editTags({ msgId }, cb) { |
36 | cb = cb || function() {} |
37 | api.sheet.display(function (close) { |
38 | const tagsToCreate = MutantArray([]) |
39 | const tagsToApply = MutantArray([]) |
40 | const tagsToRemove = MutantArray([]) |
41 | const tagsInput = Value('') |
42 | |
43 | const myId = api.keys.sync.id() |
44 | const messageTags = map( |
45 | api.tag.obs.messageTags(msgId), |
46 | tagId => Struct({ |
47 | tagId: Value(tagId), |
48 | tagName: api.about.obs.valueFrom(tagId, 'name', myId) |
49 | }) |
50 | ) |
51 | const filteredMessages = computed( |
52 | [ messageTags, tagsToRemove ], |
53 | (tags, removedIds) => filter(tags, tag => !removedIds.includes(tag.tagId)) |
54 | ) |
55 | |
56 | const messageTagsView = map( |
57 | filteredMessages, |
58 | tag => computed(tag, t => api.tag.html.tag(t, () => tagsToRemove.push(t.tagId))) |
59 | ) |
60 | const tagsToApplyView = map( |
61 | tagsToApply, |
62 | tag => api.tag.html.tag(tag, () => tagsToApply.delete(tag)) |
63 | ) |
64 | const tagsToCreateView = map( |
65 | tagsToCreate, |
66 | tag => api.tag.html.tag({ tagName: tag, tagId: 'new' }, () => tagsToCreate.delete(tag)) |
67 | ) |
68 | const stagedTags = computed( |
69 | [messageTagsView, tagsToApplyView, tagsToCreateView], |
70 | (a, b, c) => h('StagedTags', concat(a, [b, c])) |
71 | ) |
72 | |
73 | const input = h('input.tags', { |
74 | placeholder: 'Add tags here', |
75 | 'ev-keyup': onInput, |
76 | value: tagsInput() |
77 | }) |
78 | |
79 | input.addEventListener('suggestselect', onSuggestSelect) |
80 | |
81 | return { |
82 | content: [ |
83 | stagedTags, |
84 | h('EditTags', input) |
85 | ], |
86 | footer: [ |
87 | h('button.save', { |
88 | 'ev-click': () => { |
89 | onSave() |
90 | close() |
91 | } |
92 | }, |
93 | 'Save' |
94 | ), |
95 | h('button.cancel', { |
96 | 'ev-click': () => close() |
97 | }, |
98 | 'Cancel' |
99 | ) |
100 | ], |
101 | mounted: () => { |
102 | input.focus() |
103 | addSuggest(input, (inputText, cb) => { |
104 | cb(null, getTagSuggestions(inputText)) |
105 | }, { cls: 'ConfirmSuggest' }) |
106 | } |
107 | } |
108 | |
109 | function publish () { |
110 | close() |
111 | onSave() |
112 | } |
113 | |
114 | function onInput(e) { |
115 | const input = e.target.value; |
116 | if (!input.endsWith(",")) { |
117 | tagsInput.set(input) |
118 | return |
119 | } |
120 | const tag = input.substring(0, input.length - 1) |
121 | tagsToCreate.push(tag) |
122 | e.target.value = "" |
123 | } |
124 | |
125 | function onSuggestSelect(e) { |
126 | e.target.value = "" |
127 | const { value, tagId } = e.detail |
128 | const index = tagsToRemove().indexOf(tagId) |
129 | if (index >= 0) { |
130 | tagsToRemove.deleteAt(index) |
131 | } else { |
132 | tagsToApply.push({ tagId, tagName: value }) |
133 | } |
134 | } |
135 | |
136 | function getTagSuggestions(word) { |
137 | const suggestions = map( |
138 | api.tag.obs.allTags(), |
139 | tagId => { |
140 | const tagName = api.about.obs.valueFrom(tagId, 'name', myId)() |
141 | return { |
142 | title: tagName, |
143 | value: tagName, |
144 | tagId |
145 | } |
146 | } |
147 | )() |
148 | const appliedTagIds = map(filteredMessages, tag => tag.tagId) |
149 | const applyTagIds = map(tagsToApply, tag => tag.tagId) |
150 | const stagedTagIds = computed([ appliedTagIds, applyTagIds ], (a, b) => concat(a, b))() |
151 | const filteredSuggestions = filter(suggestions, tag => !stagedTagIds.includes(tag.tagId)) |
152 | filteredSuggestions.push({ title: "Press , to create a new tag" }) |
153 | return filteredSuggestions |
154 | } |
155 | |
156 | function onSave() { |
157 | // tagsToCreate |
158 | forEach( |
159 | tagsToCreate(), |
160 | tag => { |
161 | api.tag.async.create(null, (err, msg) => { |
162 | if (err) return |
163 | api.tag.async.name({ tag: msg.key, name: tag }, cb) |
164 | api.tag.async.apply({ tagged: true, message: msgId, tag: msg.key }, cb) |
165 | }) |
166 | } |
167 | ) |
168 | |
169 | // tagsToApply |
170 | forEach( |
171 | tagsToApply(), |
172 | tag => api.tag.async.apply({ tagged: true, message: msgId, tag: tag.tagId }, cb) |
173 | ) |
174 | |
175 | // tagsToRemove |
176 | forEach( |
177 | tagsToRemove(), |
178 | tagId => api.tag.async.apply({ tagged: false, message: msgId, tag: tagId }, cb) |
179 | ) |
180 | } |
181 | }) |
182 | } |
183 | } |
Built with git-ssb-web