From 09c3c35c3302f1134cb404a2b7aad2aa7eb3792b Mon Sep 17 00:00:00 2001 From: s-ol Date: Sat, 6 Feb 2021 12:46:13 +0100 Subject: Mastodon semi-compatibility --- src/cors-proxy.js | 12 +++++++ src/index.js | 105 +++++++++++++++++++++++++++++++++++++++++++++--------- src/layout.js | 2 ++ src/ui/Note.js | 11 ++++-- 4 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 src/cors-proxy.js (limited to 'src') diff --git a/src/cors-proxy.js b/src/cors-proxy.js new file mode 100644 index 0000000..905eb0f --- /dev/null +++ b/src/cors-proxy.js @@ -0,0 +1,12 @@ +var host = process.env.HOST || '127.0.0.1'; +var port = process.env.PORT || 8088; + +var cors_proxy = require('cors-anywhere'); +cors_proxy.createServer({ + originWhitelist: [], // Allow all origins + requireHeader: ['origin', 'x-requested-with'], + removeHeaders: ['cookie', 'cookie2'], + checkRateLimit: null, +}).listen(port, host, function() { + console.log('Running CORS Anywhere on ' + host + ':' + port); +}); 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 = ( ( main { font-family: serif; } +section > main > p:first-child { + margin-top: 0; +} +section > main > p:last-child { + margin-bottom: 0; +} + section.tombstone > main { font-family: inherit; } @@ -153,9 +160,7 @@ export const Note = ({ ?
:
} -
- {content} -
+
); }; -- cgit v1.2.3