git ssb

3+

arj / patchbook



Tree: 40aa5761d29866aa4ab08ff139a33262cbc14177

Files: 40aa5761d29866aa4ab08ff139a33262cbc14177 / book / html / layout / detail.js

6898 bytesRaw
1const nest = require('depnest')
2const { h, when, computed, Value } = require('mutant')
3var htmlEscape = require('html-escape')
4const addSuggest = require('suggest-box')
5
6exports.needs = nest({
7 'book.obs.book': 'first',
8 'about.html.image': 'first',
9 'about.obs.name': 'first',
10 'keys.sync.id': 'first',
11 'emoji.async.suggest': 'first',
12 'emoji.sync.url': 'first',
13 'message.html': {
14 'markdown': 'first'
15 },
16 'book.html': {
17 'title': 'first',
18 'authors': 'first',
19 'description': 'first',
20 'images': 'first'
21 }
22})
23
24exports.gives = nest('book.html.layout')
25
26exports.create = (api) => {
27 return nest('book.html.layout', bookLayout)
28
29 function renderEmoji (emoji, url) {
30 if (!url) return ':' + emoji + ':'
31 return `
32 <img
33 src="${htmlEscape(url)}"
34 alt=":${htmlEscape(emoji)}:"
35 title=":${htmlEscape(emoji)}:"
36 class="emoji"
37 >
38 `
39 }
40
41 function simpleMarkdown(text) {
42 if (text.startsWith(':'))
43 return renderEmoji(text, api.emoji.sync.url(text.match(/:([^:]*)/)[1]))
44 else
45 return text
46 }
47
48 function ratingEdit(isEditing, value) {
49 return when(isEditing,
50 h('input', {
51 'ev-input': e => value.set(e.target.value),
52 value,
53 placeholder: 'your rating' // REVIEW - I spread the keys over several lines to make this easier to read
54 }),
55 h('span.text', { innerHTML: computed(value, simpleMarkdown) })
56 )
57 }
58
59 function ratingTypeEdit(isEditing, value) {
60 let getEmojiSuggestions = api.emoji.async.suggest()
61
62 let ratingTypeInput = h('input', {'ev-input': e => value.set(e.target.value),
63 value: value, placeholder: 'rating type' })
64
65 let suggestWrapper = h('span.ratingType', ratingTypeInput)
66
67 addSuggest(ratingTypeInput, (inputText, cb) => {
68 if (inputText[0] === ':') {
69 cb(null, getEmojiSuggestions(inputText.slice(1)))
70 }
71 }, {cls: 'PatchSuggest'})
72
73 ratingTypeInput.addEventListener('suggestselect', ev => {
74 value.set(ev.detail.value)
75 })
76
77 return when(isEditing, suggestWrapper,
78 h('span.text', { innerHTML: computed(value, simpleMarkdown) }))
79 }
80
81 function simpleEdit(isEditing, name, value) {
82 // REVIEW - I split out logic into new lines when it's muddying the html areas
83 // REVIEW - don't use when + computed (when is just like a special sort of computed... jus use computed and role your own
84 const classList = computed([value, isEditing], (v, e) => {
85 return v || e
86 ? '-expanded'
87 : '-contracted'
88 })
89
90 return h('div', { classList }, [
91 h('span', name + ':'),
92 when(isEditing,
93 h('input', {'ev-input': e => value.set(e.target.value), value }),
94 h('span', value)
95 )
96 ])
97 }
98
99 function textEdit(isEditing, name, value) {
100 const classList = computed([value, isEditing], (v, e) => {
101 return v || e
102 ? '-expanded'
103 : '-contracted'
104 })
105
106 return h('div', { classList }, [
107 h('div', name + ':'),
108 when(isEditing,
109 h('textarea', {'ev-input': e => value.set(e.target.value), value }),
110 computed(value, api.message.html.markdown)
111 )
112 ])
113 // REVIEW - refactored this to follow same pattern as function above
114 }
115
116 function bookLayout (msg, opts) {
117 if (!(opts.layout === undefined || opts.layout === 'detail')) return
118
119 const { obs, isEditing, isCard } = opts
120
121 const { title, authors, description, images } = api.book.html
122
123 let isEditingSubjective = Value(false)
124 let originalSubjective = {}
125 let originalBook = {}
126 let reviews = []
127
128 return [h('Message -book-detail', [
129 title({ title: obs.title, msg, isEditing, onUpdate: obs.title.set }),
130 authors({authors: obs.authors, isEditing, onUpdate: obs.authors.set}),
131 h('section.content', [
132 images({images: obs.images, isEditing, onUpdate: obs.images.add }),
133 h('section.description',
134 description({description: obs.description, isEditing, onUpdate: obs.description.set})),
135 ]),
136 h('section.actions', [
137 h('button.edit', { 'ev-click': () => {
138 if (isEditing()) { // cancel
139 Object.keys(originalBook).forEach((v) => {
140 obs[v].set(originalBook[v])
141 })
142 } else
143 originalBook = JSON.parse(JSON.stringify(obs()))
144
145 isEditing.set(!isEditing())
146 } },
147 when(isEditing, 'Cancel', 'Edit book')),
148 when(isEditing, h('button', {'ev-click': () => saveBook(obs)}, 'Update book'))
149 ]),
150 h('section.subjective', [
151 computed(obs.subjective, subjectives => {
152 let i = 0;
153 Object.keys(subjectives).forEach(user => {
154 if (i++ < reviews.length) return
155 let subjective = obs.subjective.get(user)
156 let isMe = Value(api.keys.sync.id() == user)
157 let isOwnEditingSubj = computed([isEditingSubjective, isMe],
158 (e, me) => { return e && me })
159 reviews.push([
160 // REVIEW - this section is hard to read, I'd get rid of the when computed and change in indenting (but that could be my personal taste)
161 h('section', [api.about.html.image(user),
162 when(computed([subjective.rating, isEditingSubjective],
163 (v, e) => { return v || e }),
164 h('span.text', [api.about.obs.name(user), ' rated '])),
165 ratingEdit(isOwnEditingSubj, subjective.rating),
166 ratingTypeEdit(isOwnEditingSubj, subjective.ratingType)]),
167 simpleEdit(isOwnEditingSubj, 'Shelve', subjective.shelve),
168 simpleEdit(isOwnEditingSubj, 'Genre', subjective.genre),
169 textEdit(isOwnEditingSubj, 'Review', subjective.review)
170 ])
171 })
172
173 return reviews
174 })
175 ]),
176 h('section.actions', [
177 h('button.subjective', {
178 'ev-click': () => {
179 if (isEditingSubjective()) { // cancel
180 let subj = obs.subjective.get(api.keys.sync.id())
181 Object.keys(originalSubjective).forEach((v) => {
182 subj[v].set(originalSubjective[v])
183 })
184 } else
185 originalSubjective = JSON.parse(JSON.stringify(obs.subjective.get(api.keys.sync.id())()))
186
187 isEditingSubjective.set(!isEditingSubjective())
188 }
189 },
190 when(isEditingSubjective, 'Cancel', 'Edit rating')),
191 when(isEditingSubjective, h('button', { 'ev-click': () => saveSubjective(obs) }, 'Update rating'))
192 ]),
193 ])]
194
195 function saveBook(obs) {
196 obs.amend()
197
198 isEditing.set(false)
199 }
200
201 function saveSubjective(obs) {
202 obs.updateSubjective()
203
204 isEditingSubjective.set(false)
205 }
206 }
207}
208

Built with git-ssb-web