view src/streamed_graph_client.ts @ 5:8aa42fa04b17

enable more code
author drewp@bigasterisk.com
date Wed, 04 Dec 2019 00:32:24 -0800
parents a668a774b162
children a5bc13dcce41
line wrap: on
line source

// from /my/site/homepage/www/rdf/streamed-graph.js

import * as async from "async";
import * as jsonld from "jsonld";

//import eachJsonLdQuad from "./json_ld_quads";
import { Store, DataFactory } from "n3"

/// <reference types="eventsource" />
const EventSource = window.EventSource;

export class StreamedGraphClient {
    onStatus: (msg: string) => void;
    onGraphChanged: () => void;
    store: Store;
    events: EventSource;
    constructor(
        eventsUrl: string,
        onGraphChanged: () => void,
        onStatus: (status: string) => void,
        prefixes: Array<Record<string, string>>,
        staticGraphUrls: Array<string>) {
        console.log('new StreamedGraph', eventsUrl);
        // holds a n3 Store, which is synced to a server-side
        // store that sends patches over SSE
        this.onStatus = onStatus;
        this.onGraphChanged = onGraphChanged;
        this.onStatus('startup...');

        this.store = new Store({});

        //     //             Object.keys(prefixes).forEach((prefix) => {
        //     //                 this.store.setPrefix(prefix, prefixes[prefix]);
        //     //             });

        this.connect(eventsUrl);
        this.reconnectOnWake();

        //     staticGraphUrls.forEach((url) => {
        //         fetch(url).then((response) => response.text())
        //             .then((body) => {
        //                 // parse with n3, add to output
        //             });
        //     });

    }

    reconnectOnWake() {
        // it's not this, which fires on every mouse-in on a browser window, and doesn't seem to work for screen-turned-back-on
        //window.addEventListener('focus', function() { this.connect(eventsUrl); }.bind(this));
    }

    connect(eventsUrl: string) {
        // need to exit here if this obj has been replaced

        this.onStatus('start connect...');
        this.close();
        if (this.events && this.events.readyState != EventSource.CLOSED) {
            this.onStatus('zombie');
            throw new Error("zombie eventsource");
        }


        this.events = new EventSource(eventsUrl);

        this.events.addEventListener('error', (ev) => {
            // todo: this is piling up tons of retries and eventually multiple connections
            // this.testEventUrl(eventsUrl);
            this.onStatus('connection lost- retrying');
            setTimeout(() => {
                requestAnimationFrame(() => {
                    this.connect(eventsUrl);
                });
            }, 3000);
        });

        this.events.addEventListener('fullGraph', (ev) => {
            this.onStatus('sync- full graph update');
            let onReplaced = () => {
                this.onStatus('synced');
                this.onGraphChanged();
            };
            this.replaceFullGraph(ev.data, onReplaced);
        });

        this.events.addEventListener('patch', (ev) => {
            this.onStatus('sync- updating');
            let onPatched = () => {
                this.onStatus('synced');
                this.onGraphChanged();
            };
            this.patchGraph(ev.data, onPatched);
        });
        this.onStatus('connecting...');
    }

    replaceFullGraph(jsonLdText: string, done: () => void) {
        // this.quadStore.clear();
        // eachJsonLdQuad(this.store.rdf, JSON.parse(jsonLdText),
        //     this.quadStore.add.bind(this.quadStore), function () {
        //         done();
        //     });
        // or this.store.insert([quad], quad.graph, function() {});
    }

    patchGraph(patchJson: string, done: () => void) {
        var patch = JSON.parse(patchJson).patch;

        // if (!this.store) {
        //     throw new Error('store ' + this.store);
        // }

        async.series([
            // (done) => {
            //     eachJsonLdQuad(this.store.rdf, patch.deletes,
            //         this.quadStore.remove.bind(this.quadStore), done);
            // },
            (done) => {
                // eachJsonLdQuad(this.store.rdf, patch.adds,
                //     this.quadStore.add.bind(this.quadStore), done);
            },
            /* seriesDone */ (done) => {
                done();
            }
        ], done);
    }

    close() {
        if (this.events) {
            this.events.close();
        }
    }

    testEventUrl(eventsUrl: string): Promise<void> {
        return new Promise<void>((resolve, reject) => {
            this.onStatus('testing connection');
            fetch(eventsUrl, {
                method: "HEAD",
                credentials: "include",
            }).then((value) => {
                if (value.status == 403) {
                    reject();
                    return;
                }
                resolve();
            }).catch((err) => {
                reject();
            });
        });
    }

}