diff options
Diffstat (limited to 'src/index.js')
| -rw-r--r-- | src/index.js | 156 |
1 files changed, 110 insertions, 46 deletions
diff --git a/src/index.js b/src/index.js index 38dc8cc..5fdda36 100644 --- a/src/index.js +++ b/src/index.js @@ -33,6 +33,7 @@ class GraphContainer extends Component { state = { name: "loading…", items: {}, + error: null, }; cache = {}; @@ -42,10 +43,22 @@ class GraphContainer extends Component { if (this.props.graph.startsWith('http')) this.loadDataMastodon(this.props.graph) - .catch(err => console.error(err)); + .catch(error => { + console.error(`Error loading Mastodon graph ${this.props.graph}:`, error); + this.setState({ + name: "error loading", + error, + }); + }); else this.loadData(this.props.graph) - .catch(err => console.error(err)); + .catch(error => { + console.error("Error loading default graph:", error); + this.setState({ + name: "error loading", + error, + }); + }); } @wrapCache @@ -79,29 +92,46 @@ class GraphContainer extends Component { } @wrapCache - async loadNote(id, items) { - const item = await jsonld.frame( - cors(id), - { + async loadNote(id, items, inReplyTo) { + let item; + try { + item = await jsonld.frame( + cors(id), + { + '@context': context, + type: 'Note', + context: { '@embed': '@never' }, + replies: { '@embed': '@always' }, + inReplyTo: { '@embed': '@never' }, + }, + { omitGraph: true } + ); + + if (item.replies.length) + item.replies = await this.loadCollection(item.replies[0].id, items, [id]); + } catch (error) { + console.error(`Error loading note '${id}':`, error); + item = { '@context': context, - type: 'Note', - context: { '@embed': '@never' }, - replies: { '@embed': '@always' }, - inReplyTo: { '@embed': '@never' }, - }, - { omitGraph: true } - ); + id, + type: 'Tombstone', + formerType: 'Note', + attributedTo: 'https://dag.s-ol.nu/users/unknown', + published: '1970-01-01T00:00:00Z', + content: `Error loading note: ${error.toString()}`, + replies: [], + inReplyTo, + }; + } item.attributedTo = await this.loadUser(item.attributedTo); - item.replies = await this.loadCollection(item.replies[0].id, items); - items[item.id] = item; return item; } async loadDataMastodon(url) { const items = {}; - const root = await this.loadNote(url, items); + const root = await this.loadNote(url, items, []); this.setState({ name: root.name || root.content, @@ -111,9 +141,7 @@ class GraphContainer extends Component { async loadData(url) { const response = await fetch(url, { - headers: { - 'Accept': 'application/json', - }, + headers: { 'Accept': 'application/json' }, }); const discussion = await jsonld.frame( await response.json(), @@ -143,13 +171,34 @@ class GraphContainer extends Component { @wrapCache async loadUser(id) { - const user = await jsonld.compact(id, context); - user.id = id; - return user; + if (id === "https://dag.s-ol.nu/users/unknown") { + return { + '@context': context, + id, + type: 'Person', + name: 'unknown', + summary: 'unknown user', + }; + } + + try { + const user = await jsonld.compact(id, context); + user.id = id; + return user; + } catch (error) { + console.error(`Error loading user '${id}':`, error); + return { + '@context': context, + id, + type: 'Person', + name: id, + summary: 'failed to load user information', + }; + } } render() { - window.s = this.state; + window.state = this.state; return this.props.render(this.state); } } @@ -213,28 +262,43 @@ class CollapseContainer extends Component { 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 }) => ( - <SelectionContainer render={({ selection, toggleSelected }) => ( - <> - <CollapseContainer - items={items} - render={({ collapsed, toggleCollapsed }) => ( - <Discussion - name={name} - items={items} - collapsed={collapsed} - toggleCollapsed={toggleCollapsed} - toggleSelected={toggleSelected} - /> - )} - /> - <Selection - items={selection.map(id => items[id])} - toggleSelected={toggleSelected} - /> - </> - )} /> - )} /> + <GraphContainer graph={graph} render={({ name, items, error }) => { + if (error) { + return ( + <article> + <h1>{name}</h1> + <p>{error.toString()}</p> + <pre> + <code> + {error.stack} + </code> + </pre> + </article> + ); + } + + return ( + <SelectionContainer render={({ selection, toggleSelected }) => ( + <> + <CollapseContainer + items={items} + render={({ collapsed, toggleCollapsed }) => ( + <Discussion + name={name} + items={items} + collapsed={collapsed} + toggleCollapsed={toggleCollapsed} + toggleSelected={toggleSelected} + /> + )} + /> + <Selection + items={selection.map(id => items[id])} + toggleSelected={toggleSelected} + /> + </> + )} /> + ); + }} /> ); render(app, document.body); -window.app = app; |
