git ssb

3+

arj / patchbook



Tree: 9c6467e267ec5e13ac657da60215077fc7b42510

Files: 9c6467e267ec5e13ac657da60215077fc7b42510 / book / html / layout / detail.js

6469 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', {'ev-input': e => value.set(e.target.value), value: value,
51 placeholder: 'your rating' }),
52 h('span.text', { innerHTML: computed(value, simpleMarkdown) }))
53 }
54
55 function ratingTypeEdit(isEditing, value) {
56 let getEmojiSuggestions = api.emoji.async.suggest()
57
58 let ratingTypeInput = h('input', {'ev-input': e => value.set(e.target.value),
59 value: value, placeholder: 'rating type' })
60
61 let suggestWrapper = h('span.ratingType', ratingTypeInput)
62
63 addSuggest(ratingTypeInput, (inputText, cb) => {
64 if (inputText[0] === ':') {
65 cb(null, getEmojiSuggestions(inputText.slice(1)))
66 }
67 }, {cls: 'PatchSuggest'})
68
69 ratingTypeInput.addEventListener('suggestselect', ev => {
70 value.set(ev.detail.value)
71 })
72
73 return when(isEditing, suggestWrapper,
74 h('span.text', { innerHTML: computed(value, simpleMarkdown) }))
75 }
76
77 function simpleEdit(isEditing, name, value) {
78 return h('div', { classList: when(computed([value, isEditing], (v, e) => { return v || e }),
79 '-expanded', '-contracted') },
80 [h('span', name + ':'),
81 when(isEditing,
82 h('input', {'ev-input': e => value.set(e.target.value), value: value }),
83 h('span', value))])
84
85 }
86
87 function textEdit(isEditing, name, value) {
88 const markdown = api.message.html.markdown
89 const input = h('textarea', {'ev-input': e => value.set(e.target.value), value: value })
90
91 return h('div', { classList: when(computed([value, isEditing], (v, e) => { return v || e }),
92 '-expanded', '-contracted') },
93 [h('div', name + ':'),
94 when(isEditing, input, computed(value, markdown))])
95 }
96
97 function bookLayout (msg, opts) {
98 if (!(opts.layout === undefined || opts.layout === 'detail')) return
99
100 const { obs, isEditing, isCard } = opts
101
102 const { title, authors, description, images } = api.book.html
103
104 let isEditingSubjective = Value(false)
105 let originalSubjective = {}
106 let originalBook = {}
107 let reviews = []
108
109 return [h('Message -book-detail', [
110 title({ title: obs.title, msg, isEditing, onUpdate: obs.title.set }),
111 authors({authors: obs.authors, isEditing, onUpdate: obs.authors.set}),
112 h('section.content', [
113 images({images: obs.images, isEditing, onUpdate: obs.images.add }),
114 h('section.description',
115 description({description: obs.description, isEditing, onUpdate: obs.description.set})),
116 ]),
117 h('section.actions', [
118 h('button.edit', { 'ev-click': () => {
119 if (isEditing()) { // cancel
120 Object.keys(originalBook).forEach((v) => {
121 obs[v].set(originalBook[v])
122 })
123 } else
124 originalBook = JSON.parse(JSON.stringify(obs()))
125
126 isEditing.set(!isEditing())
127 } },
128 when(isEditing, 'Cancel', 'Edit book')),
129 when(isEditing, h('button', {'ev-click': () => saveBook(obs)}, 'Update book'))
130 ]),
131 h('section.subjective', [
132 computed(obs.subjective, subjectives => {
133 let i = 0;
134 Object.keys(subjectives).forEach(user => {
135 if (i++ < reviews.length) return
136 let subjective = obs.subjective.get(user)
137 let isMe = Value(api.keys.sync.id() == user)
138 let isOwnEditingSubj = computed([isEditingSubjective, isMe],
139 (e, me) => { return e && me })
140 reviews.push([
141 h('section', [api.about.html.image(user),
142 when(computed([subjective.rating, isEditingSubjective],
143 (v, e) => { return v || e }),
144 h('span.text', [api.about.obs.name(user), ' rated '])),
145 ratingEdit(isOwnEditingSubj, subjective.rating),
146 ratingTypeEdit(isOwnEditingSubj, subjective.ratingType)]),
147 simpleEdit(isOwnEditingSubj, 'Shelve', subjective.shelve),
148 simpleEdit(isOwnEditingSubj, 'Genre', subjective.genre),
149 textEdit(isOwnEditingSubj, 'Review', subjective.review)
150 ])
151 })
152
153 return reviews
154 })
155 ]),
156 h('section.actions', [
157 h('button.subjective', {
158 'ev-click': () => {
159 if (isEditingSubjective()) { // cancel
160 let subj = obs.subjective.get(api.keys.sync.id())
161 Object.keys(originalSubjective).forEach((v) => {
162 subj[v].set(originalSubjective[v])
163 })
164 } else
165 originalSubjective = JSON.parse(JSON.stringify(obs.subjective.get(api.keys.sync.id())()))
166
167 isEditingSubjective.set(!isEditingSubjective())
168 }
169 },
170 when(isEditingSubjective, 'Cancel', 'Edit rating')),
171 when(isEditingSubjective, h('button', { 'ev-click': () => saveSubjective(obs) }, 'Update rating'))
172 ]),
173 ])]
174
175 function saveBook(obs) {
176 obs.amend()
177
178 isEditing.set(false)
179 }
180
181 function saveSubjective(obs) {
182 obs.updateSubjective()
183
184 isEditingSubjective.set(false)
185 }
186 }
187}
188

Built with git-ssb-web