git ssb

0+

farewellutopia-dev / patchboot



Tree: 54f45b9e3a2114fd3c234a8de046349c9a3dab1c

Files: 54f45b9e3a2114fd3c234a8de046349c9a3dab1c / src / components / AppController.js

9168 bytesRaw
1import { VotesManager } from '../VotesManager.js';
2import { IdentityManager } from '../IdentityManager.js';
3
4class AppController extends HTMLElement {
5 constructor() {
6 super();
7 }
8 connectedCallback() {
9 const appDescription = this.msg.value;
10 const votesManager = new VotesManager(this.sbot)
11 const controllerArea = this.attachShadow({ mode: 'open' });
12 controllerArea.innerHTML = `
13<style>
14 * {
15 box-sizing: border-box;
16 }
17
18 #app {
19 font-size: 16px;
20 font-family: Inter, 'Helvetica Neue', Arial, Helvetica, sans-serif;
21 border-bottom: 1px solid gray;
22 width: 100%;
23 margin: 0;
24 padding: 8px 6px 0 8px;
25 }
26
27 .bar {
28 display: grid;
29 grid-template-columns: minmax(0, 1fr) auto;
30 padding-bottom: 8px;
31 white-space: nowrap;
32 }
33
34 .info > * {
35 text-overflow: ellipsis;
36 overflow-x: hidden;
37 }
38
39 .details {
40 padding-bottom: 0;
41 overflow-x: hidden;
42 height: 0;
43 white-space: normal;
44 }
45
46 #app.expanded .info * {
47 height: unset;
48 white-space: normal;
49 }
50
51 #app.expanded .details {
52 padding-bottom: 8px;
53 height: auto;
54 }
55
56 .name {
57 font-size: 17px;
58 line-height: 20px;
59 height: 20px;
60 font-weight: 600;
61 }
62
63 #author,
64 .time,
65 #author-id {
66 font-size: 13px;
67 line-height: 16px;
68 height: 16px;
69 }
70
71 .time:not(:empty)::before {
72 content: 'Published on ';
73 color: gray;
74 }
75
76 #author-id {
77 font-family: monospace;
78 color: gray;
79 }
80
81 .details .actions {
82 float: right;
83 }
84
85 .actions {
86 display: flex;
87 margin-left: 6px;
88 }
89
90 .actions button {
91 margin: 0 2px;
92 padding: 6px;
93 border: none;
94 border-radius: 50%;
95 height: 36px;
96 background-color: #f8f8f8;
97 }
98
99 .actions button:hover {
100 background-color: #dddddd;
101 }
102
103 .actions button svg {
104 display: block;
105 height: 24px;
106 width: 24px;
107 }
108
109 .svghover .onhover {
110 display: none;
111 }
112
113 .svghover:hover path {
114 display: none;
115 }
116
117 .svghover:hover .onhover {
118 display: unset;
119 }
120
121 .hidden {
122 display: none;
123 }
124
125 .count {
126 position: relative;
127 }
128
129 .count[data-count]::before {
130 content: attr(data-count);
131 position: absolute;
132 bottom: 0;
133 left: 0;
134 font-size: 13px;
135 line-height: 13px;
136 font-weight: 600;
137 padding: 0 0 4px 4px;
138 border-top-right-radius: 4px;
139 /* color: #ff2f92; */
140 background-color: #f8f8f8;
141 background: linear-gradient(45deg, rgba(255,255,255,0) 0%, #f8f8f8 100%);
142 }
143
144 .count[data-count]:hover::before {
145 background: linear-gradient(45deg, rgba(255, 255, 255, 0) 50%, rgba(221, 221, 221, 1) 100%);
146 }
147</style>
148<div id="app">
149 <div class="bar">
150 <div class="info">
151 <div class="name">${appDescription.content.name || appDescription.content.mentions[0].name || appDescription.content.comment || ''}</div>
152 <div id="author"></div>
153 </div>
154 <div class="actions">
155 <button title="Like" id="like" class="svghover hidden count">
156 <svg width="24" viewBox="0 0 24 24">
157 <path fill="currentColor"
158 d="M12.1,18.55L12,18.65L11.89,18.55C7.14,14.24 4,11.39 4,8.5C4,6.5 5.5,5 7.5,5C9.04,5 10.54,6 11.07,7.36H12.93C13.46,6 14.96,5 16.5,5C18.5,5 20,6.5 20,8.5C20,11.39 16.86,14.24 12.1,18.55M16.5,3C14.76,3 13.09,3.81 12,5.08C10.91,3.81 9.24,3 7.5,3C4.42,3 2,5.41 2,8.5C2,12.27 5.4,15.36 10.55,20.03L12,21.35L13.45,20.03C18.6,15.36 22,12.27 22,8.5C22,5.41 19.58,3 16.5,3Z" />
159 <path class="onhover" fill="currentColor"
160 d="M12.67 20.74L12 21.35L10.55 20.03C5.4 15.36 2 12.27 2 8.5C2 5.41 4.42 3 7.5 3C9.24 3 10.91 3.81 12 5.08C13.09 3.81 14.76 3 16.5 3C19.58 3 22 5.41 22 8.5C22 9.93 21.5 11.26 20.62 12.61C20 12.31 19.31 12.11 18.59 12.04C19.5 10.8 20 9.65 20 8.5C20 6.5 18.5 5 16.5 5C14.96 5 13.46 6 12.93 7.36H11.07C10.54 6 9.04 5 7.5 5C5.5 5 4 6.5 4 8.5C4 11.39 7.14 14.24 11.89 18.55L12 18.65L12.04 18.61C12.12 19.37 12.34 20.09 12.67 20.74M17 14V17H14V19H17V22H19V19H22V17H19V14H17Z" />
161 </svg>
162 </button>
163 <button title="Unlike" id="unlike" class="svghover hidden count">
164 <svg width="24" viewBox="0 0 24 24">
165 <path fill="currentColor"
166 d="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z" />
167 <path class="onhover" fill="currentColor"
168 d="M12 18C12 19 12.25 19.92 12.67 20.74L12 21.35L10.55 20.03C5.4 15.36 2 12.27 2 8.5C2 5.41 4.42 3 7.5 3C9.24 3 10.91 3.81 12 5.08C13.09 3.81 14.76 3 16.5 3C19.58 3 22 5.41 22 8.5C22 9.93 21.5 11.26 20.62 12.61C19.83 12.23 18.94 12 18 12C14.69 12 12 14.69 12 18M14 17V19H22V17H14Z" />
169 </svg>
170 </button>
171 <button title="Run" id="run">
172 <svg width="24" viewBox="0 0 24 24">
173 <path fill="currentColor" d="M8,5.14V19.14L19,12.14L8,5.14Z" />
174 </svg>
175 </button>
176 </div>
177 </div>
178 <div class="details bar">
179 <div class="info">
180 <div class="actions">
181 <button title="Revoke App" id="revoke" class="hidden">
182 <svg width="24" viewBox="0 0 24 24">
183 <path fill="currentColor"
184 d="M8.27,3L3,8.27V15.73L8.27,21H15.73L21,15.73V8.27L15.73,3M8.41,7L12,10.59L15.59,7L17,8.41L13.41,12L17,15.59L15.59,17L12,13.41L8.41,17L7,15.59L10.59,12L7,8.41" />
185 </svg>
186 </button>
187 <button title="View Source" id="source">
188 <svg width="24" viewBox="0 0 24 24">
189 <path fill="currentColor"
190 d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" />
191 </svg>
192 </button>
193 </div>
194 <div class="comment">${appDescription.content.comment || ''}</div>
195 <div id="author-id">${appDescription.author}</div>
196 <div class="time">${(new Date(appDescription.timestamp)).toLocaleString() || ''}</div>
197 </div>
198 </div>
199</div>`
200
201 const appEl = controllerArea.getElementById('app')
202 appEl.addEventListener('click', e => {
203 appEl.classList.toggle('expanded')
204 })
205
206 const renderLikesStuff = () => {
207 votesManager.getVotes(this.msg.key).then(likes => {
208 const count = likes.length
209 if (count > 0) {
210 controllerArea.querySelectorAll('.count').forEach(e => e.setAttribute('data-count', count))
211 } else {
212 controllerArea.querySelectorAll('.count').forEach(e => e.removeAttribute('data-count'))
213 }
214 })
215
216 votesManager.getOwnVote(this.msg.key).then(liked => {
217 if (liked) {
218 this.classList.add('liked')
219 controllerArea.getElementById('like').classList.add('hidden')
220 controllerArea.getElementById('unlike').classList.remove('hidden')
221 } else {
222 this.classList.remove('liked')
223 controllerArea.getElementById('like').classList.remove('hidden')
224 controllerArea.getElementById('unlike').classList.add('hidden')
225 }
226 }, e => {
227 console.log("error getting own vote:", e)
228 })
229 }
230 renderLikesStuff()
231
232 this.sbot.whoami().then(
233 currentUser => this.msg.value.author === currentUser.id)
234 .then(own => {
235 if (own) {
236 controllerArea.getElementById('revoke').classList.remove('hidden')
237 }
238 })
239
240
241 ;(new IdentityManager(this.sbot)).getSelfAssignedName(appDescription.author).then(name => {
242 controllerArea.getElementById('author').innerHTML = name
243 }).catch(e => console.log(e));
244
245 controllerArea.getElementById('run').addEventListener('click', e => {
246 e.stopPropagation()
247 this.dispatchEvent(new Event('run'));
248 })
249 controllerArea.getRootNode().getElementById('source').addEventListener('click', e => {
250 e.stopPropagation()
251 this.dispatchEvent(new Event('view-source'));
252 })
253 controllerArea.getRootNode().getElementById('like').addEventListener('click', async e => {
254 e.stopPropagation()
255 await votesManager.vote(this.msg.key, 1)
256 renderLikesStuff()
257 this.dispatchEvent(new Event('like'))
258 })
259 controllerArea.getRootNode().getElementById('unlike').addEventListener('click', async e => {
260 e.stopPropagation()
261 await votesManager.vote(this.msg.key, 0)
262 renderLikesStuff()
263 this.dispatchEvent(new Event('unlike'))
264 })
265 controllerArea.getRootNode().getElementById('revoke').addEventListener('click', async e => {
266 e.stopPropagation()
267 if (!confirm("Revoke App? This action cannot be undone.")) {
268 return;
269 }
270 await this.revoke()
271 this.parentElement.removeChild(this)
272 this.dispatchEvent(new Event('revoked'))
273 })
274 }
275
276 revoke() {
277 return new Promise((resolve, reject) => {
278 this.sbot.publish({
279 type: 'about',
280 about: this.msg.key,
281 status: 'revoked'
282 }, function (err, msg) {
283 if (err) {
284 reject(err)
285 } else {
286 resolve(true)
287 }
288 })
289 })
290 }
291}
292
293customElements.define("app-controller", AppController);

Built with git-ssb-web