/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones import { Dataspace, Ref, Sturdy, AnyValue, Reader, Schemas, isEmbedded } 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, ForceRelayDisconnect, fromRelayAddress, RelayAddress, fromForceRelayDisconnect, Resolved } from "./wsRelay"; import { fromSays, fromPresent, Present, Says } from './gen/simpleChatProtocol'; const Transport = Schemas.transportAddress; export function main() { document.getElementById('chat_form')!.onsubmit = e => { e.preventDefault(); return false; }; document.getElementById('nym_form')!.onsubmit = e => { e.preventDefault(); return false; }; setWsurl(); 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('#wsurl', 'value', $wsurl: string) => during UIChangeableProperty('#servercap', 'value', $servercapText: string) => { let servercap: AnyValue | null = null; try { const a = new Reader(servercapText).next(); Sturdy.asSturdyRef(a); // throws if invalid servercap = a; } catch (e) { console.error(e); } assert UIAttribute('#servercap', 'class', 'invalid') when (!servercap); if (wsurl && servercap) { contactRemote(wsurl, servercap); } } function contactRemote(wsurl: string, servercap: AnyValue) { during Resolved({ "addr": RelayAddress(Transport.WebSocket(wsurl)), "sturdyref": servercap, "resolved": $remoteDs_e, }) => { if (!isEmbedded(remoteDs_e)) return; const remoteDs = remoteDs_e.embeddedValue; on message WakeEvent() => send message fromForceRelayDisconnect(ForceRelayDisconnect( RelayAddress(Transport.WebSocket(wsurl)))); outputState('connected', 'connected to ' + wsurl); on stop outputState('disconnected', 'disconnected from ' + wsurl); 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 fromSays(Says({ who: nym.value, what: utterance })); } } } at remoteDs { assert fromPresent(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 setWsurl() { const wsurl = document.getElementById('wsurl')! as HTMLInputElement; if (wsurl.value === '') { wsurl.value = `ws://${document.location.host}:9001/`; } } 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}`); }