Files: 1d5d3beb765e9f7bb08097a0896eb8ea83eeccca / book / html / layout / detail.js
6298 bytesRaw
1 | const nest = require('depnest') |
2 | const { h, when, computed, Value } = require('mutant') |
3 | const addSuggest = require('suggest-box') |
4 | |
5 | exports.needs = nest({ |
6 | 'book.obs.book': 'first', |
7 | 'about.html.image': 'first', |
8 | 'about.obs.name': 'first', |
9 | 'keys.sync.id': 'first', |
10 | 'emoji.async.suggest': 'first', |
11 | 'message.html': { |
12 | markdown: 'first' |
13 | }, |
14 | 'book.html': { |
15 | title: 'first', |
16 | authors: 'first', |
17 | series: 'first', |
18 | description: 'first', |
19 | images: 'first', |
20 | simpleEmoji: 'first' |
21 | } |
22 | }) |
23 | |
24 | exports.gives = nest('book.html.layout') |
25 | |
26 | exports.create = (api) => { |
27 | return nest('book.html.layout', bookLayout) |
28 | |
29 | function ratingEdit(isEditing, value) { |
30 | return when(isEditing, |
31 | h('input', { |
32 | 'ev-input': e => value.set(e.target.value), |
33 | value, |
34 | placeholder: 'your rating' |
35 | }), |
36 | h('span.text', { innerHTML: computed(value, api.book.html.simpleEmoji) }) |
37 | ) |
38 | } |
39 | |
40 | function ratingTypeEdit(isEditing, value) { |
41 | let getEmojiSuggestions = api.emoji.async.suggest() |
42 | |
43 | let ratingTypeInput = h('input', {'ev-input': e => value.set(e.target.value), |
44 | value: value, placeholder: 'rating type' }) |
45 | |
46 | let suggestWrapper = h('span.ratingType', ratingTypeInput) |
47 | |
48 | addSuggest(ratingTypeInput, (inputText, cb) => { |
49 | if (inputText[0] === ':') { |
50 | cb(null, getEmojiSuggestions(inputText.slice(1))) |
51 | } |
52 | }, {cls: 'PatchSuggest'}) |
53 | |
54 | ratingTypeInput.addEventListener('suggestselect', ev => { |
55 | value.set(ev.detail.value) |
56 | }) |
57 | |
58 | return when(isEditing, suggestWrapper, |
59 | h('span.text', { innerHTML: computed(value, api.book.html.simpleEmoji) })) |
60 | } |
61 | |
62 | function simpleEdit(isEditing, name, value) { |
63 | const classList = computed([value, isEditing], (v, e) => { |
64 | return v || e |
65 | ? '-expanded' |
66 | : '-contracted' |
67 | }) |
68 | |
69 | return h('div', { classList }, [ |
70 | h('span', name + ':'), |
71 | when(isEditing, |
72 | h('input', {'ev-input': e => value.set(e.target.value), value }), |
73 | h('span', value) |
74 | ) |
75 | ]) |
76 | } |
77 | |
78 | function textEdit(isEditing, name, value) { |
79 | const classList = computed([value, isEditing], (v, e) => { |
80 | return v || e |
81 | ? '-expanded' |
82 | : '-contracted' |
83 | }) |
84 | |
85 | return h('div', { classList }, [ |
86 | h('div', name + ':'), |
87 | when(isEditing, |
88 | h('textarea', {'ev-input': e => value.set(e.target.value), value }), |
89 | computed(value, api.message.html.markdown) |
90 | ) |
91 | ]) |
92 | } |
93 | |
94 | function saveSubjective(obs, isEditingSubjective) { |
95 | obs.updateSubjective() |
96 | isEditingSubjective.set(false) |
97 | } |
98 | |
99 | function handleSubjective(user, i, obs) { |
100 | let originalSubjective = {} |
101 | let isEditingSubjective = Value(false) |
102 | let subjective = obs.subjective.get(user) |
103 | let isMe = Value(api.keys.sync.id() == user) |
104 | let isOwnEditingSubj = computed([isEditingSubjective, isMe], |
105 | (e, me) => { return e && me }) |
106 | let showRating = computed([subjective.rating, isEditingSubjective, isMe], |
107 | (v, e, me) => { return v || (e && me) }) |
108 | |
109 | function editRatingClick() { |
110 | if (isEditingSubjective()) { // cancel |
111 | if (obs.subjective.has(api.keys.sync.id())) { |
112 | let subj = obs.subjective.get(api.keys.sync.id()) |
113 | Object.keys(originalSubjective).forEach((v) => { |
114 | subj[v].set(originalSubjective[v]) |
115 | }) |
116 | } |
117 | } else { |
118 | if (obs.subjective.has(api.keys.sync.id())) |
119 | originalSubjective = JSON.parse(JSON.stringify(obs.subjective.get(api.keys.sync.id())())) |
120 | } |
121 | |
122 | isEditingSubjective.set(!isEditingSubjective()) |
123 | } |
124 | |
125 | return [ |
126 | h('section', |
127 | [api.about.html.image(user), |
128 | h('span.text', [api.about.obs.name(user), when(showRating, ' rated ')]), |
129 | ratingEdit(isOwnEditingSubj, subjective.rating), |
130 | ratingTypeEdit(isOwnEditingSubj, subjective.ratingType)]), |
131 | simpleEdit(isOwnEditingSubj, 'Shelve', subjective.shelve), |
132 | simpleEdit(isOwnEditingSubj, 'Genre', subjective.genre), |
133 | textEdit(isOwnEditingSubj, 'Review', subjective.review), |
134 | h('section.actions', |
135 | when(isMe, [ |
136 | h('button.subjective', { 'ev-click': editRatingClick }, |
137 | when(isEditingSubjective, 'Cancel', 'Edit my rating')), |
138 | when(isEditingSubjective, |
139 | h('button', { 'ev-click': () => saveSubjective(obs, isEditingSubjective) }, |
140 | 'Update rating')) |
141 | ])) |
142 | ] |
143 | } |
144 | |
145 | function bookLayout(msg, opts) { |
146 | const { layout, obs, isEditing, isCard } = opts |
147 | |
148 | if (layout !== undefined && layout !== 'detail') return |
149 | |
150 | const { title, authors, description, |
151 | series, seriesNo, images } = api.book.html |
152 | |
153 | let originalBook = {} |
154 | let reviews = [] |
155 | |
156 | function editClick() { |
157 | if (isEditing()) { // cancel |
158 | Object.keys(originalBook).forEach((v) => { |
159 | obs[v].set(originalBook[v]) |
160 | }) |
161 | } else |
162 | originalBook = JSON.parse(JSON.stringify(obs())) |
163 | |
164 | isEditing.set(!isEditing()) |
165 | } |
166 | |
167 | return [h('Message -book-detail', [ |
168 | title({ title: obs.title, msg, isEditing, onUpdate: obs.title.set }), |
169 | series({ series: obs.series, seriesNo: obs.seriesNo, msg, isEditing, |
170 | onUpdate: obs.series.set, onUpdateNo: obs.seriesNo.set }), |
171 | authors({authors: obs.authors, isEditing, onUpdate: obs.authors.set}), |
172 | h('section.content', [ |
173 | images({images: obs.images, isEditing, onUpdate: obs.images.add }), |
174 | h('section.description', |
175 | description({description: obs.description, isEditing, onUpdate: obs.description.set})), |
176 | ]), |
177 | h('section.actions', [ |
178 | h('button.edit', { 'ev-click': editClick }, |
179 | when(isEditing, 'Cancel', 'Edit book')), |
180 | when(isEditing, h('button', {'ev-click': () => saveBook(obs)}, 'Update book')) |
181 | ]), |
182 | h('section.subjective', [ |
183 | computed(obs.subjective, subjectives => { |
184 | let i = 0; |
185 | Object.keys(subjectives).forEach(user => { |
186 | if (i++ < reviews.length) return |
187 | reviews.push(handleSubjective(user, i, obs)) |
188 | }) |
189 | |
190 | return reviews |
191 | }) |
192 | ]) |
193 | ])] |
194 | |
195 | function saveBook(obs) { |
196 | obs.amend() |
197 | |
198 | isEditing.set(false) |
199 | } |
200 | } |
201 | } |
202 |
Built with git-ssb-web