git ssb

1+

Daan Patchwork / patchwork



Tree: 4257408311e6d56b2f491a8f7f5166f45e2ddec5

Files: 4257408311e6d56b2f491a8f7f5166f45e2ddec5 / lib / depject / sheet / editTags.js

5198 bytesRaw
1const nest = require('depnest')
2const { h, Value, map, computed, Array: MutantArray } = require('mutant')
3const concat = require('lodash.concat')
4const filter = require('lodash.filter')
5const parallel = require('run-parallel')
6const addSuggest = require('suggest-box')
7const TagHelper = require('scuttle-tag')
8const ref = require('ssb-ref')
9const catchLinks = require('../../catch-links')
10const displaySheet = require('../../sheet/display')
11
12exports.gives = nest('sheet.editTags')
13
14exports.needs = nest({
15 'keys.sync.id': 'first',
16 'sbot.obs.connection': 'first',
17 'tag.async.suggest': 'first',
18 'tag.html.tag': 'first',
19 'intl.sync.i18n': 'first',
20 'app.navigate': 'first',
21 'message.html.link': 'first'
22})
23
24exports.create = function (api) {
25 const i18n = api.intl.sync.i18n
26 return nest({ 'sheet.editTags': editTags })
27
28 function editTags ({ msgId }, callback) {
29 const ScuttleTag = TagHelper(api.sbot.obs.connection)
30 const HtmlTag = api.tag.html.tag
31
32 callback = callback || function () {}
33
34 displaySheet(function (close) {
35 const { content, onMount, onSave } = edit({ msgId })
36
37 const wrapper = h('div', [
38 h('h2', {
39 style: { 'font-weight': 'normal', 'text-align': 'center' }
40 }, [i18n('Add / Edit Tags'), ': ', api.message.html.link(msgId, { inContext: true })]),
41 content
42 ])
43
44 catchLinks(wrapper, (href, external, anchor) => {
45 if (!external) {
46 api.app.navigate(href, anchor)
47 }
48 })
49
50 return {
51 content: wrapper,
52 footer: [
53 h('button.save', { 'ev-click': publish }, i18n('Save')),
54 h('button.cancel', { 'ev-click': close }, i18n('Cancel'))
55 ],
56 onMount
57 }
58
59 function publish () {
60 close()
61 onSave()
62 }
63 })
64
65 function edit ({ msgId }) {
66 const tagsToCreate = MutantArray([])
67 const tagsToApply = MutantArray([])
68 const tagsToRemove = MutantArray([])
69 const tagsInput = Value('')
70
71 const myId = api.keys.sync.id()
72 const messageTags = ScuttleTag.obs.messageTagsFrom(msgId, myId)
73 const filteredTags = computed([messageTags, tagsToRemove], (tagIds, removedIds) =>
74 filter(tagIds, tagId => !removedIds.includes(tagId)))
75
76 const messageTagsView = map(filteredTags, tagId =>
77 HtmlTag(tagId, { onRemove: () => tagsToRemove.push(tagId) }))
78 const tagsToApplyView = map(tagsToApply, tagId =>
79 HtmlTag(tagId, { onRemove: () => tagsToApply.delete(tagId) }))
80 const tagsToCreateView = map(tagsToCreate, tag =>
81 HtmlTag('new', { nameFn: () => tag, onRemove: () => tagsToCreate.delete(tag) }))
82 const stagedTags = computed([messageTagsView, tagsToApplyView, tagsToCreateView], (a, b, c) =>
83 h('StagedTags', concat(a, [b, c])))
84
85 const input = h('input.tags', {
86 placeholder: i18n('Add tags here'),
87 'ev-keyup': onInput,
88 value: tagsInput()
89 })
90
91 input.addEventListener('suggestselect', onSuggestSelect)
92
93 return {
94 content: [stagedTags, h('EditTags', input)],
95 onMount,
96 onSave
97 }
98
99 function onMount () {
100 input.focus()
101 const stagedTagIds = computed([filteredTags, tagsToApply], (a, b) => concat(a, b))
102 const getTagSuggestions = api.tag.async.suggest(stagedTagIds)
103 addSuggest(input, (inputText, cb) => {
104 getTagSuggestions(inputText, cb)
105 }, { cls: 'SuggestBox' })
106 }
107
108 function onInput (e) {
109 const input = e.target.value
110 if (!input.endsWith(',')) {
111 tagsInput.set(input)
112 return
113 }
114 const tag = input.substring(0, input.length - 1)
115 tagsToCreate.push(ref.normalizeChannel(tag))
116 e.target.value = ''
117 }
118
119 function onSuggestSelect (e) {
120 e.target.value = ''
121 const { value, tagId } = e.detail
122 if (!tagId) {
123 tagsToCreate.push(ref.normalizeChannel(value))
124 return
125 }
126 const index = tagsToRemove().indexOf(tagId)
127 if (index >= 0) {
128 tagsToRemove.deleteAt(index)
129 } else {
130 tagsToApply.push(tagId)
131 }
132 }
133
134 function onSave () {
135 parallel(
136 concat(
137 tagsToCreate().map(tag => cb => createNameAndApplyTag(tag, cb)),
138 tagsToApply().map(tagId => cb => ScuttleTag.async.apply({
139 tagged: true,
140 message: msgId,
141 tag: tagId
142 }, cb)),
143 tagsToRemove().map(tagId => cb => ScuttleTag.async.apply({
144 tagged: false,
145 message: msgId,
146 tag: tagId
147 }, cb))
148 ),
149 callback
150 )
151 }
152 }
153
154 function createNameAndApplyTag (tag, callback) {
155 ScuttleTag.async.create(null, (err, msg) => {
156 if (err) {
157 callback(err)
158 return
159 }
160 parallel([
161 cb => ScuttleTag.async.name({
162 tag: msg.key,
163 name: tag
164 }, cb),
165 cb => ScuttleTag.async.apply({
166 tagged: true,
167 message: msgId,
168 tag: msg.key
169 }, cb)
170 ], callback)
171 })
172 }
173 }
174}
175

Built with git-ssb-web