aboutsummaryrefslogtreecommitdiffstats
path: root/src/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/index.js')
-rw-r--r--src/index.js105
1 files changed, 88 insertions, 17 deletions
diff --git a/src/index.js b/src/index.js
index b749a53..1169307 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,3 +1,4 @@
+import 'preact/debug';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import * as jsonld from 'jsonld';
@@ -12,26 +13,98 @@ const context = [
},
];
+function wrapCache(target) {
+ const orig = target.descriptor.value;
+
+ target.descriptor.value = function (id, ...args) {
+ this.cache[id] ||= orig.call(this, id, ...args);
+ return this.cache[id];
+ };
+
+ return target;
+};
+
class GraphContainer extends Component {
state = {
name: "loading…",
- items: [],
+ items: {},
};
- usersCache = {};
+ cache = {};
constructor(props) {
super(props);
- this.loadData(this.props.graph)
- .catch(err => console.error(err));
+ if (this.props.graph.startsWith('http'))
+ this.loadDataMastodon(this.props.graph)
+ .catch(err => console.error(err));
+ else
+ this.loadData(this.props.graph)
+ .catch(err => console.error(err));
}
- async loadData(url) {
- // temporary hack
- if (url.startsWith('http'))
- url = `https://cors-anywhere.herokuapp.com/${url}`;
+ @wrapCache
+ async loadCollection(id, ...args) {
+ const collection = await jsonld.compact(`http://localhost:8088/${id}`, context);
+ let items = [];
+
+ const addItems = (i) => {
+ if (Array.isArray(i))
+ items = items.concat(i);
+ else if (i)
+ items.push(i);
+ };
+
+ // discover items
+ if (collection.items) {
+ addItems(collection.items);
+ } else {
+ let page = collection.first;
+ const seen = [];
+ while (seen.indexOf(page.id) < 0) {
+ seen.push(page.id);
+ addItems(page.items);
+
+ page = await jsonld.compact(`http://localhost:8088/${page.next}`, context);
+ }
+ }
+
+ // dereference items
+ return await Promise.all(items.map(item => this.loadNote(item.id ?? item, ...args)));
+ }
+
+ @wrapCache
+ async loadNote(id, items) {
+ const item = await jsonld.frame(
+ `http://localhost:8088/${id}`,
+ {
+ '@context': context,
+ type: 'Note',
+ context: { '@embed': '@never' },
+ replies: { '@embed': '@always' },
+ inReplyTo: { '@embed': '@never' },
+ },
+ { omitGraph: true }
+ );
+
+ item.attributedTo = await this.loadUser(item.attributedTo);
+ item.replies = await this.loadCollection(item.replies[0].id, items);
+
+ items[id] = item;
+ return item;
+ }
+
+ async loadDataMastodon(url) {
+ const items = {};
+ const root = await this.loadNote(url, items);
+ this.setState({
+ name: root.name || root.content,
+ items,
+ });
+ }
+
+ async loadData(url) {
const response = await fetch(url, {
headers: {
'Accept': 'application/json',
@@ -63,18 +136,15 @@ class GraphContainer extends Component {
this.setState(discussion);
}
+ @wrapCache
async loadUser(id) {
- if (!this.usersCache[id]) {
- this.usersCache[id] = jsonld.compact(id, context).then((user) => {
- user.id = id;
- return user;
- });
- }
-
- return await this.usersCache[id];
+ const user = await jsonld.compact(id, context);
+ user.id = id;
+ return user;
}
render() {
+ window.s = this.state;
return this.props.render(this.state);
}
}
@@ -114,7 +184,8 @@ class CollapseContainer extends Component {
}
}
-const graph = document.location.hash ? document.location.hash.substr(1) : 'lib/graph.json';
+const search = new URLSearchParams(window.location.search);
+const graph = search.has('graph') ? search.get('graph') : 'lib/graph.json';
const app = (
<GraphContainer graph={graph} render={({ name, items }) => (
<CollapseContainer