/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones import { fromJS, Bytes, Dataspace, Ref, Sturdy, AnyValue, Reader, Schemas, Embedded, stringify } from "@syndicate-lang/core"; import { boot as bootHtml, Anchor, template as html, HtmlFragments, GlobalEvent, UIAttribute, UIChangeableProperty } from "@syndicate-lang/html"; import { boot as bootWakeDetector, WakeEvent } from "./wake-detector"; import { boot as bootWsRelay } from "@syndicate-lang/ws-relay"; import { Present, Says } from './gen/simpleChatProtocol'; import G = Schemas.gatekeeper; import N = Schemas.noise; export function main() { document.getElementById('chat_form')!.onsubmit = e => { e.preventDefault(); return false; }; document.getElementById('nym_form')!.onsubmit = e => { e.preventDefault(); return false; }; setDataspaceAddress(); setUsernameIfUnset(); document.getElementById('chat_input')!.focus(); Dataspace.boot(ds => { bootHtml(ds); bootWakeDetector(ds); bootWsRelay(ds, true); bootChat(ds); }); } function bootChat(ds: Ref) { spawn named 'chat' { at ds { field nym: string = ''; on asserted UIChangeableProperty('#nym', 'value', $v: string) => nym.value = v; during UIChangeableProperty('#route', 'value', $routeText: string) => { let route: G.Route | null = null; try { route = G.asRoute(new Reader(routeText).next()); } catch (e) { console.error(e); } assert UIAttribute('#route', 'class', 'invalid') when (!route); if (route) contactRemote(route); } function contactRemote(route: G.Route) { console.log('contactRemote', route); during G.ResolvePath({ "route": route, "addr": $addr, "control": $control_e: Embedded, "resolved": G.Resolved.accepted($remoteDs_e: Embedded), }) => { const remoteDs = remoteDs_e.embeddedValue; const control = control_e.embeddedValue; on message WakeEvent() => at control { send message G.ForceDisconnect(); } outputState('connected', 'connected to ' + stringify(addr)); on stop outputState('disconnected', 'disconnected from ' + stringify(addr)); on message GlobalEvent('#send_chat', 'click', _) => { const inp = document.getElementById("chat_input") as HTMLInputElement; var utterance = inp.value; inp.value = ''; if (utterance) { at remoteDs { send message Says({ who: nym.value, what: utterance }); } } } at remoteDs { assert Present(nym.value); const ui = new Anchor(); during Present($who: string) => at ds { assert ui.context(who).html('#nymlist', html`
  • ${who}
  • `); } on message Says({ "who": $who: string, "what": $what: string }) => { outputUtterance(who, what); } } } } } } } function setDataspaceAddress() { const route = document.getElementById('route')! as HTMLInputElement; if (route.value === '') { const isSecure = (document.location.protocol ?? '').toLowerCase().endsWith('s:'); const localWs = isSecure ? 'wss' : 'ws'; const wsurls = [ `wss://generic-dataspace.demo.leastfixedpoint.com/`, `${localWs}://${document.location.hostname}:9001/`, ]; const transports: AnyValue[] = wsurls.map(u => fromJS(Schemas.transportAddress.WebSocket(u))); route.value = stringify(G.Route({ "transports": transports, "pathSteps": [G.PathStep({ "stepType": N.$noise, "detail": fromJS(N.NoiseSpec({ "service": "syndicate", "key": Bytes.fromHex("21f6cd4e11e7e37711d6b3084ff18cded8fc8abf293aa47d43e8bb86dda65516"), "protocol": N.NoiseProtocol.absent(), "preSharedKeys": N.NoisePreSharedKeys.absent(), })), })], })); // To use a sturdyref instead: // // route.value = stringify(G.Route({ // "transports": transports, // "pathSteps": [G.PathStep({ // "stepType": Sturdy.$ref, // "detail": fromJS(Sturdy.Parameters({ // "oid": "syndicate", // "sig": Bytes.fromHex('69ca300c1dbfa08fba692102dd82311a'), // "caveats": Sturdy.CaveatsField.absent(), // })), // })], // })); // // ... and of course you can chain these things, depending on server setup. } } function setUsernameIfUnset() { const nym = document.getElementById('nym')! as HTMLInputElement; if (nym.value === '') { nym.value = "nym" + Math.floor(Math.random() * 65536); } } function outputItem(cls: string, item0: HtmlFragments): ChildNode { const stamp = html`${(new Date()).toUTCString()}`; const item = html`
    ${stamp}${item0}
    `; const n = item.node(); const o = document.getElementById('chat_output')!; o.appendChild(n); o.scrollTop = o.scrollHeight; return n; } function outputState(cls: string, state: string) { outputItem(`state_${cls}`, html`${state}`) } function outputUtterance(who: string, what: string) { outputItem('utterance', html`${who}${what}`); }