aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dom.js48
-rw-r--r--src/index.js86
-rw-r--r--src/ui.js89
3 files changed, 154 insertions, 69 deletions
diff --git a/src/dom.js b/src/dom.js
new file mode 100644
index 0000000..9e44d0a
--- /dev/null
+++ b/src/dom.js
@@ -0,0 +1,48 @@
+export const css = ([ code] ) => {
+ const style = document.createElement('style');
+ document.head.appendChild(style);
+ style.appendChild(document.createTextNode(''));
+
+ let i = 0;
+ for (const v of code.split('}')) {
+ if (v.indexOf('{') < 0) continue;
+ style.sheet.insertRule(v + '}', i++);
+ }
+
+ return style;
+};
+
+const appendChild = (parent, child) => {
+ if (Array.isArray(child)) {
+ child.forEach((nestedChild) => appendChild(parent, nestedChild));
+ } else {
+ parent.appendChild(child.nodeType ? child : document.createTextNode(child));
+ }
+}
+
+const objectProps = { style: true, dataset: true };
+
+export const createElement = (tag, props, ...children) => {
+ if ('function' === typeof tag)
+ return tag(props, children);
+
+ const element = document.createElement(tag);
+
+ for (const [name, value] of Object.entries(props || {})) {
+ if (name.startsWith('on') && name.toLowerCase() in window) {
+ element.addEventListener(name.toLowerCase().substr(2), value);
+ } else if (objectProps[name]) {
+ Object.assign(element[name], value);
+ } else {
+ element.setAttribute(name, value.toString());
+ }
+ }
+
+ for (const child of children) {
+ appendChild(element, child);
+ }
+
+ return element;
+}
+
+export const createFragment = (props, ...children) => children;
diff --git a/src/index.js b/src/index.js
index 254821a..187464b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -2,49 +2,15 @@ import 'core-js/stable';
import 'regenerator-runtime/runtime';
import * as jsonld from 'jsonld';
import dagre from 'dagre';
-import ColorHash from 'color-hash';
+import * as dom from './dom';
+import { Note } from './ui';
-const css = ([ code] ) => {
- const style = document.createElement('style');
- document.head.appendChild(style);
- style.appendChild(document.createTextNode(''));
-
- let i = 0;
- for (const v of code.split('}')) {
- if (v.indexOf('{') < 0) continue;
- style.sheet.insertRule(v + '}', i++);
- }
-
- return style;
-};
-
-css`
+dom.css`
article {
position: relative;
}
-
-section {
- position: absolute;
- overflow: hidden;
-
- width: 350px;
-
- background: #696969;
- border-radius: 8px;
-}
-
-section > header {
- padding: 0.75em 1em 0.5em;
- color: #121212;
-}
-
-section > main {
- padding: 1em;
- color: #eeeeee;
-}
`;
-const colors = new ColorHash({ lightness: 0.8 });
const context = 'https://www.w3.org/ns/activitystreams';
const userCache = {};
@@ -54,6 +20,7 @@ const loadUser = async (id) => {
const result = await jsonld.compact(id, context);
userCache[id] = result;
+ result.icon.url = new URL(result.icon.url, id).href;
return result;
}
@@ -71,31 +38,6 @@ const loadData = async () => {
const article = document.createElement('article');
document.body.appendChild(article);
-const createNode = (data) => {
- const root = document.createElement('section');
- root.dataset.dagId = data.id;
-
- const header = document.createElement('header');
- header.style.backgroundColor = data.attributedTo.color || colors.hex(data.attributedTo.id);
- const author = document.createElement('span');
- author.textContent = data.attributedTo.name || data.attributedTo.preferredUsername;
- header.appendChild(author);
- root.appendChild(header);
-
- const main = document.createElement('main');
- main.innerHTML = data.content;
- root.appendChild(main);
-
- article.appendChild(root);
-
- return {
- data,
- root,
- width: root.offsetWidth,
- height: root.offsetHeight,
- };
-};
-
loadData().then(data => {
window.dagData = data;
const graph = new dagre.graphlib.Graph();
@@ -107,14 +49,20 @@ loadData().then(data => {
graph.setGraph({});
graph.setDefaultEdgeLabel(() => ({}));
- let rootNode;
+ let rootId;
for (const data of data['@graph']) {
- const node = createNode(data);
- graph.setNode(data.id, node);
+ const dom = <Note {...data} />;
+ article.appendChild(dom);
+
+ graph.setNode(data.id, {
+ dom, data,
+ width: dom.offsetWidth,
+ height: dom.offsetHeight,
+ });
if (!data.inReplyTo || !data.inReplyTo.length)
- rootNode = node;
+ rootId = data.id;
if (data.replies && !Array.isArray(data.replies))
data.replies = [ data.replies ];
@@ -124,7 +72,6 @@ loadData().then(data => {
}
}
- console.log(rootNode);
dagre.layout(graph);
const info = graph.graph();
@@ -133,8 +80,8 @@ loadData().then(data => {
for (const id of graph.nodes()) {
const node = graph.node(id);
- node.root.style.left = `${node.x - node.width / 2}px`;
- node.root.style.top = `${node.y - node.height / 2}px`;
+ node.dom.style.left = `${node.x - node.width / 2}px`;
+ node.dom.style.top = `${node.y - node.height / 2}px`;
}
const ctx = canvas.getContext('2d');
@@ -153,5 +100,6 @@ loadData().then(data => {
ctx.stroke();
}
+ const rootNode = graph.node(rootId);
window.scrollTo(rootNode.x - window.innerWidth/2, rootNode.y);
});
diff --git a/src/ui.js b/src/ui.js
new file mode 100644
index 0000000..10755e6
--- /dev/null
+++ b/src/ui.js
@@ -0,0 +1,89 @@
+import ColorHash from 'color-hash';
+import { formatRelative } from 'date-fns';
+import * as dom from './dom';
+
+dom.css`
+body {
+ background: #1e1e1e;
+ font-family: sans-serif;
+}
+`;
+
+const now = new Date();
+const Time = ({ time }) => {
+ const dt = new Date(time);
+ return formatRelative(dt, now);
+};
+
+dom.css`
+.flex-space {
+ flex: 1;
+}
+`;
+
+const Space = () => <div class="flex-space" />;
+
+dom.css`
+section {
+ position: absolute;
+ overflow: hidden;
+
+ width: 350px;
+
+ background: #363636;
+ border-radius: 0.4em;
+}
+
+section > header {
+ display: flex;
+ flex-direction: row;
+
+ height: 2.5em;
+ line-height: 2.5em;
+
+ color: #121212;
+}
+
+section > header > * {
+ margin: 0 0.5em;
+}
+
+section > header > img {
+ height: 2em;
+ width: 2em;
+ margin: 0.25em;
+ border-radius: 0.2em;
+}
+
+section > header > .time {
+ font-size: 0.8em;
+ color: #363636;
+}
+
+section > main {
+ padding: 1em;
+ color: #eeeeee;
+ font-family: serif;
+}
+`;
+
+const colors = new ColorHash({ lightness: 0.8 });
+export const Note = ({ id, attributedTo, published, content }) => {
+ const backgroundColor = attributedTo.color || colors.hex(attributedTo.id);
+ const username = attributedTo.name || attributedTo.preferredUsername;
+
+ return (
+ <section dataset={{ id }}>
+ <header style={{ backgroundColor }}>
+ <img src={attributedTo.icon.url} />
+ <span class="user">{username}</span>
+ <Space />
+ <span class="time"><Time time={published} /></span>
+ </header>
+ <main>
+ {content}
+ </main>
+ </section>
+ );
+};
+