Initial commit
This commit is contained in:
parent
e19790b891
commit
170431ff3e
11
README.md
11
README.md
|
@ -1,11 +0,0 @@
|
|||
# oasis
|
||||
oasis
|
||||
|
||||
## Commands
|
||||
Command | Description |
|
||||
-----------------------|--------------------------------------------------|
|
||||
`$ npm start` | Start the development server
|
||||
`$ npm test` | Lint, validate deps & run tests
|
||||
`$ npm run build` | Compile all files into `dist/`
|
||||
`$ npm run create` | Generate a scaffold file
|
||||
`$ npm run inspect` | Inspect the bundle's dependencies
|
BIN
assets/icon.png
BIN
assets/icon.png
Binary file not shown.
Before Width: | Height: | Size: 302 B |
98
index.js
98
index.js
|
@ -1,18 +1,88 @@
|
|||
var css = require('sheetify')
|
||||
var choo = require('choo')
|
||||
const Koa = require('koa')
|
||||
const path = require('path')
|
||||
const pull = require('pull-stream')
|
||||
const router = require('koa-router')()
|
||||
const views = require('koa-views')
|
||||
const cooler = require('./lib/cooler')
|
||||
const md = require('ssb-markdown')
|
||||
const lodash = require('lodash')
|
||||
const prettyMs = require('pretty-ms')
|
||||
|
||||
css('tachyons')
|
||||
const app = module.exports = new Koa()
|
||||
|
||||
var app = choo()
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
app.use(require('choo-devtools')())
|
||||
} else {
|
||||
app.use(require('choo-service-worker')())
|
||||
app.use(views(path.join(__dirname, 'views'), {
|
||||
map: { html: 'swig' }
|
||||
}))
|
||||
|
||||
router
|
||||
.get('/', home)
|
||||
|
||||
app.use(router.routes())
|
||||
|
||||
async function home (ctx) {
|
||||
var ssb = await cooler.connect()
|
||||
var whoami = await cooler.get(ssb.whoami)
|
||||
|
||||
const userName = await cooler.get(
|
||||
ssb.about.socialValue, {
|
||||
key: 'name',
|
||||
dest: whoami.id
|
||||
}
|
||||
)
|
||||
|
||||
var msgSource = await cooler.read(
|
||||
ssb.messagesByType, {
|
||||
type: 'post',
|
||||
reverse: true,
|
||||
limit: 32
|
||||
}
|
||||
)
|
||||
const rawMsgs = await new Promise((resolve, reject) => {
|
||||
pull(
|
||||
msgSource,
|
||||
pull.collect((err, msgs) => {
|
||||
if (err) return reject(err)
|
||||
resolve(msgs)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
const msgs = await Promise.all(rawMsgs.map(async (msg) => {
|
||||
lodash.set(msg, 'value.meta.md.block', () =>
|
||||
md.block(msg.value.content.text)
|
||||
)
|
||||
|
||||
const name = await cooler.get(
|
||||
ssb.about.socialValue, { key: 'name',
|
||||
dest: msg.value.author
|
||||
}
|
||||
)
|
||||
|
||||
const avatarMsg = await cooler.get(
|
||||
ssb.about.socialValue, { key: 'image',
|
||||
dest: msg.value.author
|
||||
}
|
||||
)
|
||||
|
||||
const avatarId = avatarMsg != null && typeof avatarMsg.link === 'string'
|
||||
? avatarMsg.link
|
||||
: avatarMsg
|
||||
|
||||
const avatarUrl = `http://localhost:8989/blobs/get/${avatarId}`
|
||||
|
||||
const ts = new Date(msg.value.timestamp)
|
||||
lodash.set(msg, 'value.meta.timestamp.received.iso8601', ts.toISOString())
|
||||
lodash.set(msg, 'value.meta.timestamp.received.since', prettyMs(Date.now() - ts, { compact: true }))
|
||||
lodash.set(msg, 'value.meta.author.name', name)
|
||||
lodash.set(msg, 'value.meta.author.avatar', {
|
||||
id: avatarId,
|
||||
url: avatarUrl
|
||||
})
|
||||
|
||||
return msg
|
||||
}))
|
||||
|
||||
await ctx.render('home', { whoami, msgs, userName })
|
||||
}
|
||||
|
||||
app.use(require('./stores/clicks'))
|
||||
|
||||
app.route('/', require('./views/main'))
|
||||
app.route('/*', require('./views/404'))
|
||||
|
||||
module.exports = app.mount('body')
|
||||
if (!module.parent) app.listen(3000)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
const ssbClient = require('ssb-client')
|
||||
|
||||
// a water cooler API
|
||||
module.exports = {
|
||||
connect: function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
ssbClient((err, api) => {
|
||||
if (err) reject(err)
|
||||
resolve(api)
|
||||
})
|
||||
})
|
||||
},
|
||||
get: function (method, ...opts) {
|
||||
return new Promise((resolve, reject) => {
|
||||
method(...opts, (err, val) => {
|
||||
if (err) return reject(err)
|
||||
resolve(val)
|
||||
})
|
||||
})
|
||||
},
|
||||
read: function (method, ...args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(method(...args))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "oasis",
|
||||
"short_name": "oasis",
|
||||
"description": "oasis",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#000",
|
||||
"theme_color": "#000",
|
||||
"icons": [{
|
||||
"src": "/assets/icon.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
|
@ -1,26 +1,21 @@
|
|||
{
|
||||
"name": "oasis",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "bankai build index.js",
|
||||
"create": "choo-scaffold",
|
||||
"inspect": "bankai inspect index.js",
|
||||
"start": "bankai start index.js",
|
||||
"test": "standard && npm run test-deps",
|
||||
"test-deps": "dependency-check . && dependency-check . --extra --no-dev -i tachyons"
|
||||
},
|
||||
"main": "index.js",
|
||||
"author": "Christian Bundy <christianbundy@fraction.io>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"choo": "^6.13.3",
|
||||
"choo-service-worker": "^3.0.0",
|
||||
"sheetify": "^7.4.0",
|
||||
"tachyons": "^4.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bankai": "^9.15.0",
|
||||
"choo-devtools": "^3.0.0",
|
||||
"choo-scaffold": "^1.2.0",
|
||||
"dependency-check": "^3.3.0",
|
||||
"standard": "^12.0.1"
|
||||
"ejs": "^2.6.2",
|
||||
"handlebars": "^4.1.2",
|
||||
"hbs": "^4.0.4",
|
||||
"koa": "^2.7.0",
|
||||
"koa-router": "^7.4.0",
|
||||
"koa-views": "^6.2.0",
|
||||
"lodash": "^4.17.11",
|
||||
"pretty-ms": "^5.0.0",
|
||||
"pull-stream": "^3.6.12",
|
||||
"ssb-client": "^4.7.7",
|
||||
"ssb-markdown": "^5.0.1",
|
||||
"swig": "^1.4.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
module.exports = store
|
||||
|
||||
function store (state, emitter) {
|
||||
state.totalClicks = 0
|
||||
|
||||
emitter.on('DOMContentLoaded', function () {
|
||||
emitter.on('clicks:add', function (count) {
|
||||
state.totalClicks += count
|
||||
emitter.emit(state.events.RENDER)
|
||||
})
|
||||
})
|
||||
}
|
28
sw.js
28
sw.js
|
@ -1,28 +0,0 @@
|
|||
/* eslint-env serviceworker */
|
||||
|
||||
var VERSION = require('./package.json').version
|
||||
var URLS = process.env.FILE_LIST
|
||||
|
||||
// Respond with cached resources
|
||||
self.addEventListener('fetch', function (e) {
|
||||
e.respondWith(self.caches.match(e.request).then(function (request) {
|
||||
if (request) return request
|
||||
else return self.fetch(e.request)
|
||||
}))
|
||||
})
|
||||
|
||||
// Register worker
|
||||
self.addEventListener('install', function (e) {
|
||||
e.waitUntil(self.caches.open(VERSION).then(function (cache) {
|
||||
return cache.addAll(URLS)
|
||||
}))
|
||||
})
|
||||
|
||||
// Remove outdated resources
|
||||
self.addEventListener('activate', function (e) {
|
||||
e.waitUntil(self.caches.keys().then(function (keyList) {
|
||||
return Promise.all(keyList.map(function (key, i) {
|
||||
if (keyList[i] !== VERSION) return self.caches.delete(keyList[i])
|
||||
}))
|
||||
}))
|
||||
})
|
15
views/404.js
15
views/404.js
|
@ -1,15 +0,0 @@
|
|||
var html = require('choo/html')
|
||||
|
||||
var TITLE = 'oasis - route not found'
|
||||
|
||||
module.exports = view
|
||||
|
||||
function view (state, emit) {
|
||||
if (state.title !== TITLE) emit(state.events.DOMTITLECHANGE, TITLE)
|
||||
return html`
|
||||
<body class="sans-serif pa3">
|
||||
<h1>Route not found.</h1>
|
||||
<a class="pt2" href="/">Back to main.</a>
|
||||
</body>
|
||||
`
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
{% extends 'layout.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% for msg in msgs %}
|
||||
<section class="message">
|
||||
<header>
|
||||
<img class="avatar" src="{{ msg.value.meta.author.avatar.url }}">
|
||||
|
||||
<span class="text">
|
||||
<span class="author">
|
||||
{{ msg.value.meta.author.name}}
|
||||
</span>
|
||||
|
||||
<span class="timestamp">
|
||||
({{ msg.value.meta.timestamp.received.since }})
|
||||
</span>
|
||||
|
||||
{% if msg.value.content.root == null %}
|
||||
🌱
|
||||
{% endif %}
|
||||
</span>
|
||||
</header>
|
||||
|
||||
<section class="content">
|
||||
{{ msg.value.meta.md.block() }}
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<a href="ssb:{{ msg.key }}">link</a>
|
||||
{% if msg.value.content.root %}
|
||||
<a href="ssb:{{ msg.value.content.root }}">root</a>
|
||||
{% endif %}
|
||||
|
||||
{% if msg.value.content.fork %}
|
||||
<a href="ssb:{{ msg.value.content.fork }}">fork</a>
|
||||
{% endif %}
|
||||
</footer>
|
||||
|
||||
</section>
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Blog</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,90 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{% block title %}Oasis{% endblock %}</title>
|
||||
<style>
|
||||
html {
|
||||
font-family: system-ui,
|
||||
-apple-system, BlinkMacSystemFont,
|
||||
"Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans",
|
||||
"Droid Sans", "Helvetica Neue", sans-serif;
|
||||
line-height: 1.5;
|
||||
font-size: 12pt;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* https://www.desmos.com/calculator/3elcf5cwhn */
|
||||
h1 { font-size: 150%; }
|
||||
h2 { font-size: 128%; }
|
||||
h3 { font-size: 120%; }
|
||||
h4 { font-size: 116%; }
|
||||
h5 { font-size: 113%; }
|
||||
h6 { font-size: 111%; }
|
||||
|
||||
h1 {
|
||||
margin-bottom: 0.5em;
|
||||
border-bottom: 1px solid #0000001a;
|
||||
}
|
||||
|
||||
.message pre {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.message code {
|
||||
color: hsl(330, 75%, 50%)
|
||||
}
|
||||
|
||||
@media screen {
|
||||
html {
|
||||
min-height: 100%;
|
||||
background: hsl(240, 100%, 99%);
|
||||
}
|
||||
body {
|
||||
max-width: 40rem;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
background: #fff;
|
||||
padding: 1.5rem;
|
||||
margin: 2rem 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #ddd;
|
||||
|
||||
}
|
||||
.message > header {
|
||||
height: 1.5rem;
|
||||
display: flex;
|
||||
}
|
||||
.message > header > .avatar {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
.message > header > .text > .author {
|
||||
margin-left: 0.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
.message > footer > a {
|
||||
color: #888;
|
||||
margin-right: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section id="content">
|
||||
{% block content %}
|
||||
<p>Missing content!</p>
|
||||
{% endblock %}
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
158
views/main.js
158
views/main.js
|
@ -1,158 +0,0 @@
|
|||
var html = require('choo/html')
|
||||
|
||||
var TITLE = 'oasis - main'
|
||||
|
||||
module.exports = view
|
||||
|
||||
function view (state, emit) {
|
||||
if (state.title !== TITLE) emit(state.events.DOMTITLECHANGE, TITLE)
|
||||
|
||||
return html`
|
||||
<body class="code lh-copy">
|
||||
<main class="pa3 cf center">
|
||||
<section class="fl mw6 w-50-m w-third-l pa3">
|
||||
<h2>1.</h2>
|
||||
<p>
|
||||
Welcome to your new Choo application.
|
||||
We're very happy you've made it this far.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You're now in control of your own Choo app. The moment you decide to
|
||||
deploy it, it'll work offline and on any device.
|
||||
</p>
|
||||
|
||||
<br>
|
||||
</section>
|
||||
|
||||
<section class="fl mw6 w-50-m w-third-l pa3">
|
||||
<h2>2.</h2>
|
||||
|
||||
<p>
|
||||
We've outfitted your project with a small selection of commands to
|
||||
help you achieve results faster:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li class="mb3">
|
||||
<strong>npm start</strong><br>
|
||||
start your project for local development.
|
||||
</li>
|
||||
<li class="mb3">
|
||||
<strong>npm run build</strong><br>
|
||||
compile your project for production.
|
||||
</li>
|
||||
<li class="mb3">
|
||||
<strong>npm run inspect</strong><br>
|
||||
visualize your project's dependencies.
|
||||
</li>
|
||||
<li class="mb3">
|
||||
<strong>npm run create</strong><br>
|
||||
scaffold a new file.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
</section>
|
||||
|
||||
<section class="fl mw6 w-50-m w-third-l pa3">
|
||||
<h2>3.</h2>
|
||||
|
||||
<p>
|
||||
Your project also comes with a few directories. These names have
|
||||
special meanings for the build tool, so it's good to know what they
|
||||
do.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li class="mb3">
|
||||
<strong>assets/</strong><br>
|
||||
Static files that can be served up, such as images and fonts.
|
||||
</li>
|
||||
<li class="mb3">
|
||||
<strong>components/</strong><br>
|
||||
Reusable fragments that can be composed into views.
|
||||
</li>
|
||||
<li class="mb3">
|
||||
<strong>stores/</strong><br>
|
||||
Pieces of logic that are shared by multiple components.
|
||||
</li>
|
||||
<li class="mb3">
|
||||
<strong>views/</strong><br>
|
||||
Combinations of components that are mapped to routes.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
</section>
|
||||
|
||||
<section class="fl mw6 w-50-m w-third-l pa3">
|
||||
<h2>4.</h2>
|
||||
|
||||
<p>
|
||||
So far we've provided you with one base view, <a
|
||||
href="/oh-no">one fallback view</a>, and one store. This serves
|
||||
as an example. A place to start from. It's your project now, so
|
||||
go ahead and delete them once you know how they work.
|
||||
</p>
|
||||
|
||||
<p>Number of clicks stored: ${state.totalClicks}</p>
|
||||
|
||||
<button class="dim ph3 ba bw1 pv2 b--black pointer bg-white"
|
||||
onclick=${handleClick}>
|
||||
Emit a click event
|
||||
</button>
|
||||
|
||||
<br><br>
|
||||
</section>
|
||||
|
||||
<section class="fl mw6 w-50-m w-third-l pa3">
|
||||
<h2>5.</h2>
|
||||
|
||||
<p>
|
||||
To make your development journey more pleasant, we've also
|
||||
included <a
|
||||
href="https://github.com/choojs/choo-devtools">devtools</a>. If
|
||||
you open your browser console, here's a selection of the
|
||||
commands that are at your disposal:
|
||||
|
||||
<ul>
|
||||
<li class="mb3">
|
||||
<strong>choo.state</strong><br>
|
||||
Log the current application state.
|
||||
</li>
|
||||
<li class="mb3">
|
||||
<strong>choo.log</strong><br>
|
||||
Log the last 150 events received by the event bus.
|
||||
</li>
|
||||
<li class="mb3">
|
||||
<strong>choo.emit</strong><br>
|
||||
Emit an event inside the application event bus.
|
||||
</li>
|
||||
<li class="mb3">
|
||||
<strong>choo.help</strong><br>
|
||||
See an overview of all available commands.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="fl mw6 w-50-m w-third-l pa3">
|
||||
<h2>6.</h2>
|
||||
|
||||
<p>
|
||||
And that's about it! Thanks for reading. If you have any
|
||||
questions, check out the <a href="https://choo.io">docs</a> or reach
|
||||
out on <a href="https://github.com/choojs/choo">GitHub</a> or <a
|
||||
href="https://www.irccloud.com/irc/freenode/channel/choo">IRC</a>.
|
||||
We're online everyday, and always around to help. Happy hacking!
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
`
|
||||
|
||||
function handleClick () {
|
||||
emit('clicks:add', 1)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue