Files: 1721af4ce8e5508c633c4f6b2731dbf3e6d6f66b / gossip-zine.org
30716 bytesRaw
1 | #+NAME: Local Gossip Zine |
2 | #+AUTHOR: Zach Mandeville |
3 | #+EMAIL: webmaster@coolguy.website |
4 | |
5 | * Introduction |
6 | |
7 | This is a hypertextual origami frog of a write-up for a scrycast zine in the chorus. This page goes over the inspirations for the zine, what we trying to do and where we going, and includes the code for the zine itself. Our aim is to help teach how to build one of these zines yrself using the tools we like, and to give the reason for whyt he code be the way it is. |
8 | |
9 | BUT! this here page also transforms directly into a scrycast zine. just run the scripts in your setup heading, type the magic keys, and watch the whole thing blossom. |
10 | |
11 | * Intentions |
12 | |
13 | Put simply this will be a zine that holds an audio player that will play the mp3 of our scrycast. The zine template is meant to be easy to reproduce but also, importantly, easy to collaborate on. I want the file structure to be easy enough to follow on something like git-ssb or gitlab, so people can put in their own bios or transcriptions or anything else. |
14 | |
15 | My dream is for Dan or Glyph to say, "New podcast coming up. the link for it is here: feel free to add a trasncription or, if yr one of the peers, yr bio and image. |
16 | |
17 | In the future, I wanna get even more ridiculous and have minute by minute annotations possible. So as yr listening to the show, a small note pops up with additional info. These notes, then, can be collaborated upon by others. |
18 | |
19 | The site is a pleasant and minimal looking player but holds a pleasantly surprising amount of functionality. |
20 | *** [40%]Cool Parts: |
21 | - [X] The Site details are all taken from a text file in the dat folder. In this way, you can just fork and replace the file and you'll have a new dat site. |
22 | - [ ] The 'audio' part of the info.txt file is a link to the url which can be local or remote. In this way, people don't //have// to host the audio if they don't want to. |
23 | - [X] There's also just a 'show notes' section you can use to put in add'l details. |
24 | - [ ] The file includes 'annotations' that are named after specific minute'second counters. Then, when it hits that part of the track the specific annotation shows up on the screen (like pop-up video). |
25 | - [ ] The bottom of the screen shows the show-notes plus the annotations (if they there) plus a button for the full transcript (if available.) When you click on either you get a bunch more details. (again, taken from a text file.) |
26 | |
27 | * Setup |
28 | ** Download Font |
29 | We only want ttf and woff, so we unzip and delete the rest! |
30 | #+NAME: Download Font |
31 | #+BEGIN_SRC shell :results output verbatim drawer :dir ./ |
32 | mkdir -p aesthetic/fonts/mononoki |
33 | wget -O aesthetic/fonts/mononoki/mononoki.zip 'https://github.com/madmalik/mononoki/releases/download/1.2/mononoki.zip' |
34 | cd aesthetic/fonts/mononoki |
35 | unzip mononoki.zip |
36 | rm *.eot |
37 | rm *.woff2 |
38 | rm *.zip |
39 | tree |
40 | #+END_SRC |
41 | |
42 | #+RESULTS: Download Font |
43 | :RESULTS: |
44 | Archive: mononoki.zip |
45 | inflating: mononoki-Bold.eot |
46 | inflating: mononoki-Bold.ttf |
47 | inflating: mononoki-Bold.woff |
48 | inflating: mononoki-Bold.woff2 |
49 | inflating: mononoki-BoldItalic.eot |
50 | inflating: mononoki-BoldItalic.ttf |
51 | inflating: mononoki-BoldItalic.woff |
52 | inflating: mononoki-BoldItalic.woff2 |
53 | inflating: mononoki-Italic.eot |
54 | inflating: mononoki-Italic.ttf |
55 | inflating: mononoki-Italic.woff |
56 | inflating: mononoki-Italic.woff2 |
57 | inflating: mononoki-Regular.eot |
58 | inflating: mononoki-Regular.ttf |
59 | inflating: mononoki-Regular.woff |
60 | inflating: mononoki-Regular.woff2 |
61 | . |
62 | ├── mononoki-Bold.ttf |
63 | ├── mononoki-Bold.woff |
64 | ├── mononoki-BoldItalic.ttf |
65 | ├── mononoki-BoldItalic.woff |
66 | ├── mononoki-Italic.ttf |
67 | ├── mononoki-Italic.woff |
68 | ├── mononoki-Regular.ttf |
69 | └── mononoki-Regular.woff |
70 | |
71 | 0 directories, 8 files |
72 | :END: |
73 | |
74 | * Parts |
75 | ** Bedrock |
76 | *** languages |
77 | This template is made with: |
78 | -[[https://choo.io][ Choo]] |
79 | - [[https://github.com/jondashkyle/smarkt][smarkt]] |
80 | - html |
81 | - css |
82 | - javascript |
83 | - love |
84 | |
85 | Choo handles the front-end modular design and is chosen cos it's small, easy to reason with, and cute. |
86 | |
87 | Smarkt is 'hyper-readable markup'. It's a way of writing content for pages that looks super clean, but then parses nicely into JSON. So what we want is to have files that people can update from the backend(or through github) that gets translated magichooly into a well-designed page! |
88 | |
89 | *** File Structure |
90 | The overall structure looks like so: |
91 | #+BEGIN_SRC sh :dir ./ :results output list |
92 | tree -I 'node_modules' . |
93 | #+END_SRC |
94 | |
95 | #+RESULTS: |
96 | #+begin_example |
97 | - . |
98 | - ├── bundle.js |
99 | - ├── dat.json |
100 | - ├── gossip-zine.org |
101 | - ├── index.html |
102 | - ├── index.js |
103 | - ├── package-lock.json |
104 | - ├── package.json |
105 | - ├── show |
106 | - │ ├── bios |
107 | - │ │ ├── angelica-blevins.txt |
108 | - │ │ └── zach.txt |
109 | - │ ├── episode.txt |
110 | - │ ├── show-audio.mp3 |
111 | - │ ├── show.txt |
112 | - │ └── solarpunk-couple.jpg |
113 | - ├── stores |
114 | - │ └── core.js |
115 | - └── views |
116 | - ├── audioPlayer.js |
117 | - ├── cast.js |
118 | - ├── episode.js |
119 | - ├── header.js |
120 | - ├── peerList.js |
121 | - ├── title.js |
122 | - └── transcription.js |
123 | - 4 directories, 21 files |
124 | #+end_example |
125 | |
126 | **** Initiatory Files |
127 | The basic choo app is made up of an index.html page that has a bundle.js page linekd into it. This bundle speaks of an index.js page, and it is from that index.js page that the rest of the site blossoms. The rest of the folders are helpful conventions, intended to make the site easy to reason about. |
128 | |
129 | The show folder holds text files, mp3's, and pictures. This is the main content and, importantly, requires no coding. A person should be able to edit a text file knowing nothing but how to click an edit pencil and use a keyboard. This is actually a lot harder than it'd appear: to make the rest of the template sturdy enough that these files can be //anything.// and a person can't accidentally break the zine. |
130 | #+NAME: index.js |
131 | #+BEGIN_SRC js js :tangle index.js |
132 | const choo = require('choo') |
133 | const devtools = require('choo-devtools') |
134 | |
135 | const cast = require('./views/cast') |
136 | |
137 | // Initialize choo and bring along it's console helper. |
138 | var app = choo() |
139 | app.use(devtools()) |
140 | |
141 | // Our Stores |
142 | app.use(require('./stores/core')) |
143 | |
144 | // Our Routes |
145 | app.route('/', cast) |
146 | |
147 | //Plant that seed. |
148 | app.mount('body') |
149 | #+END_SRC |
150 | |
151 | #+NAME: index.html |
152 | #+BEGIN_SRC html :tangle index.html |
153 | <!doctype html> |
154 | <html> |
155 | <head> |
156 | <title>Scrycast!</title> |
157 | <link rel='stylesheet' href='aesthetic/colors-and-fonts.css' type='text/css'> |
158 | <link rel='stylesheet' href='aesthetic/main.css' type='text/css'> |
159 | </head> |
160 | <body> |
161 | <h1>Loading...</h1> |
162 | <script src='bundle.js' type='application/javascript'></script> |
163 | </body> |
164 | </html> |
165 | #+END_SRC |
166 | |
167 | **** Text Files for Content |
168 | We are using Smarkt to handle the text files. It's awesome because it makes things super readable-- you have a name, a colon, some text and separate each part with four dashes (----). But it is also particular with indentations, and if you try to fit too much the indentations will be off somewhere and you'll screw up the parsing. |
169 | |
170 | The choice I made was to break up the show into different text files, each organized around some concern. So there's the basic show info--title, license, description--then there's general info about the episode, and then there's bios for all the guests. I originally had the bios included in the episode file, but it became super hard to read and work on without knowing how smarkt works---and I dont' want that. |
171 | |
172 | I also want the files to be easy enough to name. So the show info shoudl probably be changed to just 'info.txt', as show and episode seem too similar. The bios should be able to be any name...they are in the bios folder, which connects them, and they have a name portion in the file. As long as both of those are true it shouldn't matter if someone puts 'angelica.txt' 'angblev.txt' or 'ab.txt'. They all point to Angelica. |
173 | **** Choo Style Zine Template |
174 | Choo lets you build out a whole page in javascript. I'm still kinda conflicted on having this site be javascript driven. Static pages would be faster and allow folks who have javascript turned off to still be able to see our zines. At the same time, there isn't a way to turn off js within beaker, so it's really just folks who are intentionally using a browser that can read dat but can't take js---and i think that's a niche market that doesn't represent disenfranchised people. |
175 | |
176 | Also, being JS driven lets us do the magic of 'update a text file and it's auto-updated in the zine'. It lets you not worry about the code and still be able to make stuff your own. To do this with a static site would require you to 'rebuild' the page after every chagne. This would require the zinester to know how to install npm and run build scripts. This is a great idea in the future, but shouldn't be the beginning requirement. |
177 | |
178 | Lastly, I think learning code is important and I see these zines as serving two purposes: sharing your work as easily and quickly as possible, and introducing you to the world of webmastering and code. If the site is just html and css, then the zinester is learning declartive languages only. In this template setup they can add their own content and style their own things--but as soon as they want to change the struture and behavior of the zine then we introduce them to the terminal, npm, and a super accessible javascript framework. |
179 | |
180 | Choo works with the notion of Stores and Views. Stores hold the data and behavior of the zine. It's kinda the brains of the template. Views hold the different presentational parts--the components or parts of the page. We could name these whatever we want (and this is something I play with in other zine templates), but since this one is intended to be far more collaborative I wanted these folders to match the Choo documentation. |
181 | |
182 | ** The Show Files |
183 | The real heart of the zine. These hold all the various information about the specific scrycast. They are intended so that someone can fork a zine and, without any code knowledge, be able to make it their own with their own episode simply by editing these files. |
184 | |
185 | I am riding a conceptual line between 'many files' or 'one big file that could get kinda complicated. I want to include all the information on the existing template though. |
186 | *** info.txt |
187 | Deals with the basic information about the show |
188 | #+BEGIN_SRC org :tangle show/info.txt |
189 | scrycast: The Local Gossip |
190 | ---- |
191 | tagline: an experiment in decentralized, collaborative audio shows ~ scrycasting |
192 | ---- |
193 | notes: Who is responsible for producing The Local Gossip? You are! Localhosts throughout The Chorus are collaborating to deepen community relationships while exploring stories, weaving tales and finding new metaphors to understand the terrains and potentials of cypherspace from their subjective vantage points. If you want to contribute to this particular zine, you can do so here! https://gitlab.com/dat-zines/scrycast-zine-template |
194 | ---- |
195 | license: All materials are free to share Creative Commons (BY-NC-SA 3.0) but happiest in cypherspace. |
196 | ---- |
197 | contribute: |
198 | gitlab: https://gitlab.com/dat-zines/scrycast-template |
199 | git-ssb: |
200 | |
201 | |
202 | |
203 | #+END_SRC |
204 | *** Episode.txt |
205 | Handles information about the specific episode, its description, hosts, dates, and so on. the ssbKey is a bit tricky cos it neeeeeeds to be in quotes other wise it isn't parsed correctly. |
206 | #+BEGIN_SRC org :tangle show/episode.txt |
207 | title: solarpunk, travel, and metaphors |
208 | ---- |
209 | number: 02 |
210 | ---- |
211 | seed date: 14 September 2018 |
212 | ---- |
213 | localHost: |
214 | name: Dan Hassan |
215 | ssbKey: '@NeB4q4Hy9IiMxs5L08oevEhivxW+/aDu/s/0SkNayi0=.ed25519' |
216 | ---- |
217 | audio: the-local-gossip-episode-2.opus |
218 | ---- |
219 | image: |
220 | src: solarpunk-couple.jpg |
221 | caption: 'The two peers at the Solarpunk Magic Computer Club in Queens' |
222 | ---- |
223 | peers: |
224 | - name: Angelica Blevins |
225 | ssbKey: '@eANNuLfzX/9rlGODXHYV8WJb+zw2h+d7YsT4vpYPvD0=.ed25519' |
226 | - name: Zach Mandeville |
227 | ssbKey: '@ZqH7Mctu/7DNInxuwl12ECjfrAKUX2tBLq1rOldNhg0=.ed25519' |
228 | #+END_SRC |
229 | *** Bios |
230 | I wanted to be able to have links and nice things for each of the guests, while keeping freedom open for how the links are rpresented (like maybe none at all), and for them to be optional as. So one thing we could do is have a bios/ folder and put in whatever bios we want per person. So for example: |
231 | |
232 | #+NAME: Angelica Bio |
233 | #+BEGIN_SRC org :tangle ./show/bios/angelica-blevins.txt |
234 | name: Angelica Blevins |
235 | ---- |
236 | bio: |
237 | |
238 | Cartoonist, zinester, yogi, and kitchen witch. Angelica has provided a unique visual element to our scuttlepelago through her ssb emojis and the art she's shared throughout the Chorus. |
239 | |
240 | You can find an example of her yoga zines here: [Kundalini Winter Workshop](dat://81f40a285e2e7706cae59db182a4f4753df24f9c033cd1aa67e743a5f52f9008/). And check out her homepage at [angblev.com](https://angblev.com) |
241 | |
242 | #+END_SRC |
243 | |
244 | #+NAME: Zach Bio |
245 | #+BEGIN_SRC org :tangle ./show/bios/zach.txt |
246 | name: Zach Mandeville |
247 | ---- |
248 | bio: |
249 | Writer, Coder, Barber, Tarotologist, and potential friend. In this episode, Dan dubbed him Scuttlebutt's poet laureate, which made Zach make a weird mouth sound and his face turned such a deep red you can hear it through yr speakers. |
250 | |
251 | Here's some more of Zach's work in the Chorus: |
252 | - [The Future Will be Technical](dat://coolguy.website/writing/the-future-will-be-technical/) |
253 | - [A Praise Chorus](dat://2295a89c2cdfb57ed91b135608627119199d5d834fbaede70a8713b2cedf6fe1/) |
254 | - [Dat Zine Library](dat://coolguy.website/projects/dat-zine-library/) |
255 | - [His Home online](dat://coolguy.website) |
256 | |
257 | #+END_SRC |
258 | |
259 | ** Stores |
260 | *** Core |
261 | :PROPERTIES: |
262 | :header-args: :noweb yes |
263 | :END: |
264 | Our core store brings in all the show files and adds them to state, so they can be displayed by our components. |
265 | |
266 | If we ever got to a spot where we were trying to do things in this template /beyond/ bring in show files, we could create another store for that. In our index.js field we just need to do an ~app.use(store)~ to both and they'll be added to our zine's state. |
267 | |
268 | #+NAME: core store |
269 | #+BEGIN_SRC js :tangle stores/core.js |
270 | const smarkt = require('smarkt') |
271 | const _ = require('lodash') |
272 | var archive = new DatArchive(window.location) |
273 | |
274 | module.exports = (state, emitter) => { |
275 | state.info = {} |
276 | state.bios = [] |
277 | state.episode = {} |
278 | |
279 | emitter.on('DOMContentLoaded', () => { |
280 | grabTxtsFromDir('show') |
281 | checkForBios('show/bios') |
282 | }) |
283 | |
284 | function grabTxtsFromDir (dir) { |
285 | archive.readdir(dir) |
286 | .then(files => { |
287 | var txts = onlyTxts(files) |
288 | emitter.emit('Texts Found', txts) |
289 | }) |
290 | } |
291 | |
292 | emitter.on('Texts Found', (texts) => { |
293 | for (var text of texts) { |
294 | emitter.emit('Map Text To State', text) |
295 | } |
296 | }) |
297 | emitter.on('Bios Found!', () => { |
298 | archive.readdir('show/bios') |
299 | .then(files => { |
300 | var bios = onlyTxts(files) |
301 | for (var bio of bios) { |
302 | mapBioToState(bio) |
303 | } |
304 | }) |
305 | }) |
306 | |
307 | emitter.on('Map Text To State', (text) => { |
308 | mapTextToState(text) |
309 | }) |
310 | |
311 | function mapTextToState (text) { |
312 | var textName = text.split('.txt')[0] |
313 | archive.readFile(`show/${text}`) |
314 | .then((contents) => { |
315 | state[textName] = smarkt.parse(contents) |
316 | emitter.emit('render') |
317 | }) |
318 | } |
319 | |
320 | |
321 | function checkForBios (dir) { |
322 | archive.stat(dir) |
323 | .then(success => emitter.emit('Bios Found!')) |
324 | .catch(data => emitter.emit('no bio')) |
325 | } |
326 | |
327 | function mapBioToState (bio) { |
328 | archive.readFile(`show/bios/${bio}`) |
329 | .then(content => { |
330 | var bioJSON = smarkt.parse(content) |
331 | state.bios = [...state.bios, bioJSON] |
332 | emitter.emit('render') |
333 | }) |
334 | } |
335 | |
336 | function onlyTxts(dir) { |
337 | return dir.filter(file => file.includes('.txt')) |
338 | } |
339 | } |
340 | #+END_SRC |
341 | #+RESULTS: core store |
342 | |
343 | This will read our different text files, if they there, and make them available to our components! |
344 | ** Views (or Components) |
345 | :PROPERTIES: |
346 | :header-args: :dir views |
347 | :END: |
348 | *** Main component (the Cast) |
349 | |
350 | This is a view that holds all the others inside of it. Essentially the body of the main route. |
351 | each subcomponent is added in using template literal strings, like so: ~${componentName(state, emit)}~ |
352 | |
353 | #+NAME: cast view |
354 | #+BEGIN_SRC js :tangle views/cast.js |
355 | const html = require('nanohtml') |
356 | |
357 | const Title = require('./title') |
358 | const Episode = require('./episode') |
359 | const Transcription = require('./transcription') |
360 | |
361 | |
362 | module.exports = (state, emit) => { |
363 | return html` |
364 | <body> |
365 | <div class='wrapper'> |
366 | ${Title(state,emit)} |
367 | ${Episode(state, emit)} |
368 | ${Transcription(state, emit)} |
369 | </div> |
370 | </body> |
371 | ` |
372 | } |
373 | #+END_SRC |
374 | |
375 | #+NAME: Main Component Styling |
376 | #+BEGIN_SRC css :tangle no |
377 | .wrapper{ |
378 | padding: 1em; |
379 | } |
380 | |
381 | #+END_SRC |
382 | |
383 | These are small fragments of html that make up our page, and can be dynamic (pulling from the state of the app to decide what to display.) |
384 | |
385 | *** Show title. |
386 | :PROPERTIES: |
387 | :header-args: :tangle ./views/title.js |
388 | :END: |
389 | I'm basing this all on the original site, and so we want to have the title and its tagline and a little note to our readers. |
390 | |
391 | #+NAME: title |
392 | #+BEGIN_SRC js |
393 | const html = require('nanohtml') |
394 | const u = require('../utilities') |
395 | |
396 | module.exports = (state, emit) => { |
397 | if (u.hasContent(state.info)) { |
398 | var info = state.info |
399 | return html` |
400 | <div id='Title'> |
401 | <h1>${info.scrycast}</h1> |
402 | <h2>${info.tagline}</h2> |
403 | <p>${info.notes}</p> |
404 | </div> |
405 | ` |
406 | } |
407 | } |
408 | #+END_SRC |
409 | |
410 | #+NAME: Title Styling |
411 | #+BEGIN_SRC css :tangle no |
412 | /* Title! |
413 | The scrycast name, tagline, and description. |
414 | ,*/ |
415 | |
416 | #Title { |
417 | width: 80%; |
418 | margin-bottom: 0.5em; |
419 | |
420 | } |
421 | |
422 | #Title h1{ |
423 | margin-bottom: 0; |
424 | } |
425 | |
426 | |
427 | #Title h2{ |
428 | margin-top: 0.25em; |
429 | font-style: italic; |
430 | } |
431 | |
432 | |
433 | #+END_SRC |
434 | *** Episode |
435 | :PROPERTIES: |
436 | :header-args: :noweb yes :tangle ./views/episode.js |
437 | :END: |
438 | |
439 | I was going to just pilfer the code from the original cast, but it didn't fully match my style of zine design. There was a lot of inline style, while I want to keep a fully external stylesheet. This is so people do not need to run npm or look through the html code to be able to adjust the style. I want them to have an easy variables page for most things, and clearly laid out CSS for the rest. |
440 | |
441 | It also used h1 tags, when I want there to only be a single h1 tag on the page. This is to help with accessibility, and the screen readers parsing over the page. |
442 | |
443 | So I'll do a redesign with these intentions in mind. |
444 | |
445 | #+NAME: episode |
446 | #+BEGIN_SRC js |
447 | const html = require('nanohtml') |
448 | const PeerList = require('./peerList.js') |
449 | const AudioPlayer = require('./audioPlayer') |
450 | const u = require('../utilities') |
451 | |
452 | module.exports = (state, emit) => { |
453 | if (u.hasContent(state.episode)) { |
454 | var episode = state.episode |
455 | return html` |
456 | <section id='Episode'> |
457 | <h2>Episode ${episode.number}: <span id='episode-title'>${episode.title}</span></h2> |
458 | ${AudioPlayer(episode.audio)} |
459 | <p>Initial Seeding Date: ${episode['seed date']}</p> |
460 | <img src='show/${episode.image.src}' alt='${episode.image.caption}' width='800px' /> |
461 | <p>${episode.image.caption}</p> |
462 | <h3 id='localHost'>Localhost</h3> |
463 | <p>${episode.localHost.name} // ( ${episode.localHost.ssbKey} )</p> |
464 | <h3 id='guests'>Guests</h3> |
465 | <ul> |
466 | ${PeerList(state)} |
467 | </ul> |
468 | </section> |
469 | ` |
470 | } |
471 | } |
472 | #+END_SRC |
473 | |
474 | #+NAME: Episode Styling |
475 | #+BEGIN_SRC css :tangle no |
476 | #Episode { |
477 | background: var(--secondary-bg); |
478 | padding: 1em; |
479 | width: 90%; |
480 | margin: auto; |
481 | } |
482 | |
483 | #+END_SRC |
484 | *** Audio Player |
485 | :PROPERTIES: |
486 | :header-args: :noweb yes :tangle ./views/audioPlayer.js |
487 | :END: |
488 | |
489 | I'ma just use the one from destroy with science and the original scrycast page. I feel it is now the aesthetic choice for chorus audio. |
490 | |
491 | #+NAME: audioPlayer |
492 | #+BEGIN_SRC js |
493 | const html = require('nanohtml') |
494 | |
495 | module.exports = (audio) => { |
496 | return html` |
497 | <audio class='Player' style='width: 400px' controls src='show/${audio}'></audio> |
498 | ` |
499 | } |
500 | |
501 | |
502 | #+END_SRC |
503 | |
504 | *** Peer List |
505 | :PROPERTIES: |
506 | :header-args: :noweb yes :tangle ./views/peerList.js |
507 | :END: |
508 | For each episode there'll be, likely, one to many peers. We don't know how many and so they get added to our episode sheet as an array. |
509 | |
510 | We'll make a component that displays a stylable list element for each peer. |
511 | |
512 | We wanted to not be bound to bios or links or contact info or anything like that. a peer may not wanna give it, or it may not be ready by the time the episode comes out. And so, we will load up the peers and then check if they have bio files. If so, we'll add the bio to their card.list. |
513 | |
514 | #+NAME: PeerList |
515 | #+BEGIN_SRC js |
516 | const html = require('nanohtml') |
517 | const md = require('markdown-it')() |
518 | const raw = require('nanohtml/raw') |
519 | |
520 | const u = require('../utilities') |
521 | const displayBioIfAvailable = require('./peerBio') |
522 | |
523 | module.exports = function peerList (state) { |
524 | if(u.hasContent(state.episode.peers)) { |
525 | const peers = state.episode.peers |
526 | return peers.map(peer => { |
527 | return html` |
528 | <li class='peerCard'> |
529 | <h4>${peer.name} // (${peer.ssbKey}</h4> |
530 | ${checkForBio(state, peer)} |
531 | </li> |
532 | ` |
533 | }) |
534 | } |
535 | |
536 | function checkForBio (state, peer) { |
537 | if (u.hasContent(state.bios)) { |
538 | return displayBioIfAvailable(state.bios, peer) |
539 | } |
540 | } |
541 | } |
542 | |
543 | #+END_SRC |
544 | |
545 | *** Peer Bio |
546 | :PROPERTIES: |
547 | :header-args: :noweb yes :tangle ./views/peerBio.js |
548 | :END: |
549 | #+NAME: PeerBio |
550 | #+BEGIN_SRC js |
551 | const html = require('nanohtml') |
552 | const md = require('markdown-it')() |
553 | const raw = require('nanohtml/raw') |
554 | |
555 | const u = require('../utilities') |
556 | |
557 | module.exports = (bios, peer) => { |
558 | if(hasBio(bios, peer)) { |
559 | var bio = grabBio(bios, peer) |
560 | return html` |
561 | ${raw(md.render(bio.bio))} |
562 | ` |
563 | } |
564 | |
565 | function hasBio (bios, peer) { |
566 | return bios.some(bio => { |
567 | var bioName = bio.name.toLowerCase() |
568 | var peerName = peer.name.toLowerCase() |
569 | return peerName === bioName |
570 | }) |
571 | } |
572 | |
573 | function grabBio (bios, peer) { |
574 | return bios.find(bio => { |
575 | var bioName = bio.name.toLowerCase() |
576 | var peerName = peer.name.toLowerCase() |
577 | return peerName === bioName |
578 | }) |
579 | } |
580 | } |
581 | |
582 | #+END_SRC |
583 | |
584 | #+RESULTS: PeerBio |
585 | |
586 | *** Transcription |
587 | :PROPERTIES: |
588 | :header-args: :noweb yes :tangle ./views/transcription.js |
589 | :END: |
590 | |
591 | This is the coolest part to me. By making our zine in this way, we can have people contribute without needing to give them access to the dat or work with some multi-person CMS or whatev's. Instead, if we are missing a file we can have the site render "this hasn't been transcribed yet, but we'd love it to be. If you'd like to help, send us a pull request at...". So then people could just upload a markdown transcript to the git page, we'd pull it in, and the scrycast page gets updated. Beauty!! |
592 | |
593 | I know we won't have a trasncript from this one, so I'ma just fake it for right now. |
594 | #+NAME: Transcription |
595 | #+BEGIN_SRC js |
596 | const html = require('nanohtml') |
597 | |
598 | module.exports = (state, emit) => { |
599 | if (state.transcript) { |
600 | return html` |
601 | <div> |
602 | <h2>Transcription</h2> |
603 | ${state.transript} |
604 | </div> |
605 | ` |
606 | } else { |
607 | return html` |
608 | <div> |
609 | <h2>Transcription</h2> |
610 | <p>This show hasn't been transcribed yet, but we'd love it to be! If you'd like to help with this, add a transcript file to our show folder on our [gitlab page](https://gitlab.com/zachmandeville/scrycast-zine)</p> |
611 | </div> |
612 | ` |
613 | } |
614 | } |
615 | |
616 | #+END_SRC |
617 | |
618 | #+RESULTS: Transcription |
619 | ** Utilities |
620 | :PROPERTIES: |
621 | :header-args: :noweb yes :tangle ./utilities/index.js |
622 | :END: |
623 | There are a couple general tricks I find myself always using, that could be shortened into functions. In some cases lodash helps solve these common problems, and when that doesn't work I call upon my own custom made utils! All are stored in ~utilities/index.js~ as there shouldn't be that many of them. |
624 | *** Check if State has content |
625 | When a component renders, it renders the stuff given to it by the state. If that bit of state is empty, then an error is thrown...but it's not useful error (saying 'such and such is not a function' or some such.) What would be better is to tell the component to only render if it has content to show. So this function checks to make sure that part of state exists and that it has something inside of it. |
626 | #+NAME: State Has Content |
627 | #+BEGIN_SRC js |
628 | function hasContent (state) { |
629 | return state !== null && Object.keys(state).length !== 0 |
630 | } |
631 | #+END_SRC |
632 | |
633 | *** Export them Modules |
634 | #+NAME: modules.export |
635 | #+BEGIN_SRC js |
636 | module.exports = {hasContent} |
637 | |
638 | #+END_SRC |
639 | |
640 | |
641 | |
642 | |
643 | |
644 | #+NAME: |
645 | ** Aesthetic |
646 | This contains the stylesheets for the whole app. I think it is best to keep it to 2, maybe 3, stylesheets. One is just color and font variables and the other is the main stylesheet. |
647 | |
648 | Each concern in the zine is commented to its own part of the stylesheet so its easier to find, but I wanted people to be able to look at one long scroll of things and learn from them. This is part of my path to making the template yr own by getting into the code. Try changing some colors, and when that goes well try changing the styles themselves. |
649 | |
650 | I also wanna move away from adding a bunch of unnecessary classes or divs. I would like the finished page to be as semantic as possible for accessibility, and for the classes to be as minimal as possible for maintenance. There's so much you can do with pseudoelements and css selectors that we should just use those! |
651 | |
652 | I've included style sections in each of the components up above, so its easier to manage the style when you looking at/working with a single component--but they all tangle into a single file in the end. The big default stuff I'm including here. |
653 | *** Colors and Fonts |
654 | :PROPERTIES: |
655 | :header-args: :noweb yes :tangle ./aesthetic/colors-and-fonts.css |
656 | :END: |
657 | #+NAME: Colors |
658 | #+BEGIN_SRC css |
659 | /* |
660 | The Colors! Change any of the values here to whichever color you like. |
661 | bg stands for 'background', 'text' stands for the color of the text. |
662 | So 'main-bg' would be the body background |
663 | and 'main-text' would be the default color of the text on the site. |
664 | ,*/ |
665 | :root { |
666 | --main-bg: #E8FDF5; |
667 | --secondary-bg: #FBF1A9; |
668 | --main-text: #A463F2; |
669 | } |
670 | #+END_SRC |
671 | |
672 | #+NAME: Fonts |
673 | #+BEGIN_SRC css |
674 | /* |
675 | Fonts! You can change the font-family being used or the default text of the site! |
676 | ,*/ |
677 | :root { |
678 | --default-size: 18px; |
679 | --primary-font: mononoki; |
680 | } |
681 | /* This is Code added to get custom fonts working. |
682 | Add yr own here if you wanna use a custom font |
683 | --------------------------------------------------*/ |
684 | |
685 | |
686 | @font-face { |
687 | font-family: 'mononoki'; |
688 | src: url('./fonts/mononoki/mononoki-Regular.woff') format('woff'), /* Pretty Modern Browsers */ |
689 | font-weight: normal; |
690 | font-style: normal; |
691 | } |
692 | |
693 | @font-face { |
694 | font-family: 'mononoki'; |
695 | src: url('./fonts/mononoki/mononoki-Bold.woff') format('woff'), /* Pretty Modern Browsers */ |
696 | url('./fonts/mononoki/mononoki-Bold.ttf') format('truetype'); /* Safari, Android, iOS */ |
697 | font-weight: bold; |
698 | font-style: normal; |
699 | } |
700 | |
701 | @font-face { |
702 | font-family: 'mononoki'; |
703 | src: url('./fonts/mononoki/mononoki-Italic.woff') format('woff'), /* Pretty Modern Browsers */ |
704 | url('./fonts/mononoki/mononoki-Italic.ttf') format('truetype'); /* Safari, Android, iOS */ |
705 | font-weight: normal; |
706 | font-style: italic; |
707 | |
708 | } |
709 | |
710 | @font-face { |
711 | font-family: 'mononoki'; |
712 | src: url('./fonts/mononoki/mononoki-BoldItalic.woff') format('woff'), /* Pretty Modern Browsers */ |
713 | url('./fonts/mononoki/mononoki-BoldItalic.ttf') format('truetype'); /* Safari, Android, iOS */ |
714 | font-weight: bold; |
715 | font-style: italic; |
716 | } |
717 | #+END_SRC |
718 | *** Main Stylesheet |
719 | :PROPERTIES: |
720 | :header-args: :noweb yes :tangle ./aesthetic/main.css |
721 | :END: |
722 | #+NAME: Main Stylesheet |
723 | #+BEGIN_SRC css |
724 | body, html { |
725 | margin: 0; |
726 | paddding: 0; |
727 | font-size: var(--default-size); |
728 | } |
729 | |
730 | body { |
731 | background: var(--main-bg); |
732 | font-family: var(--primary-font); |
733 | color: var(--main-text); |
734 | } |
735 | |
736 | <<Main Component Styling>> |
737 | <<Title Styling>> |
738 | <<Episode Styling>> |
739 | #+END_SRC |
740 | |
741 | * Process |
742 | ** TODO Peer Bios show up in the store. |
743 | ** TODO Peer Bios added to PeerCard view, but only show up if there is a bio available. |
744 | ** TODO Font Added to Zine |
745 | ** TODO Zine styled to workable degree. |
746 | ** TODO Zine shared with crew. |
747 | ** TODO Transcription Component added as optional view. |
748 | |
749 | |
750 | |
751 |
Built with git-ssb-web