diff --git a/res/decoder-ring/datatypes.js b/res/decoder-ring/datatypes.js new file mode 100644 index 000000000..93a779e07 --- /dev/null +++ b/res/decoder-ring/datatypes.js @@ -0,0 +1,107 @@ +/* + * Quick-n-dirty algebraic datatypes. + * + * These let us handle the possibility of failure without having to constantly write code to check for it. + * We can apply all of the transformations we need as if the data is present using `map`. + * If there's a None, or a FetchError, or a Pending, those are left untouched. + * + * I've used perhaps an odd bit of terminology from scalaz in `fold`. This is basically a `switch` statement: + * You pass it a set of functions to handle the various different states of the datatype, and if it finds the + * function it'll call it on its value. + * + * It's handy to have this in functional style when dealing with React as we can dispatch different ways of rendering + * really simply: + * ``` + * bundleFetchStatus.fold({ + * some: (fetchStatus) => , + * }), + * ``` + */ + + +class Optional { + static from(value) { + return value && Some.of(value) || None; + } + map(f) { + return this; + } + flatMap(f) { + return this; + } + fold({ none }) { + return none && none(); + } +} +class Some extends Optional { + constructor(value) { + super(); + this.value = value; + } + map(f) { + return Some.of(f(this.value)); + } + flatMap(f) { + return f(this.value); + } + fold({ some }) { + return some && some(this.value); + } + static of(value) { + return new Some(value); + } +} +const None = new Optional(); + +class FetchStatus { + constructor(opt = {}) { + this.opt = { at: Date.now(), ...opt }; + } + map(f) { + return this; + } + flatMap(f) { + return this; + } +} +class Success extends FetchStatus { + static of(value) { + return new Success(value); + } + constructor(value, opt) { + super(opt); + this.value = value; + } + map(f) { + return new Success(f(this.value), this.opt); + } + flatMap(f) { + return f(this.value, this.opt); + } + fold({ success }) { + return success instanceof Function ? success(this.value, this.opt) : undefined; + } +} +class Pending extends FetchStatus { + static of(opt) { + return new Pending(opt); + } + constructor(opt) { + super(opt); + } + fold({ pending }) { + return pending instanceof Function ? pending(this.opt) : undefined; + } +} +class FetchError extends FetchStatus { + static of(reason, opt) { + return new FetchError(reason, opt); + } + constructor(reason, opt) { + super(opt); + this.reason = reason; + } + fold({ error }) { + return error instanceof Function ? error(this.reason, this.opt) : undefined; + } +} diff --git a/res/decoder-ring/decoder.js b/res/decoder-ring/decoder.js new file mode 100644 index 000000000..68c6bb066 --- /dev/null +++ b/res/decoder-ring/decoder.js @@ -0,0 +1,319 @@ +class StartupError extends Error {} + +/* + * We need to know the bundle path before we can fetch the sourcemap files. In a production environment, we can guess + * it using this. + */ +async function getBundleName() { + const res = await fetch("../index.html"); + if (!res.ok) { + throw new StartupError(`Couldn't fetch index.html to prefill bundle; ${res.status} ${res.statusText}`); + } + const index = await res.text(); + return index.split("\n").map((line) => + line.match(/ + + + + + + + + + + + +

Decoder ring

+ Waiting for javascript to run... + + + diff --git a/scripts/copy-res.js b/scripts/copy-res.js index d1fb49462..6153bb9b9 100755 --- a/scripts/copy-res.js +++ b/scripts/copy-res.js @@ -63,11 +63,11 @@ const COPY_LIST = [ ["res/welcome/**", "webapp/welcome"], ["res/themes/**", "webapp/themes"], ["res/vector-icons/**", "webapp/vector-icons"], + ["res/decoder-ring/**", "webapp/decoder-ring"], ["node_modules/matrix-react-sdk/res/media/**", "webapp/media"], ["node_modules/olm/olm_legacy.js", "webapp", { directwatch: 1 }], ["./config.json", "webapp", { directwatch: 1 }], ["contribute.json", "webapp"], - ["node_modules/matrix-react-sdk/res/decoder-ring/**", "webapp/decoder-ring"], ]; const parseArgs = require('minimist');