diff options
Diffstat (limited to 'src/index.js')
| -rw-r--r-- | src/index.js | 105 |
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 |
