convert tabbar and settings to web components
This commit is contained in:
parent
b8429533df
commit
c39c9f9412
9 changed files with 366 additions and 236 deletions
13
dist/index.html
vendored
13
dist/index.html
vendored
|
@ -12,19 +12,10 @@
|
||||||
JS app lol
|
JS app lol
|
||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="map"></div>
|
|
||||||
|
|
||||||
<div id="err_wrapper" style='display:none'><button id="err_close" onclick="closeErr()">x</button><div id="err_div"></div></div>
|
<div id="err_wrapper" style='display:none'><button id="err_close" onclick="closeErr()">x</button><div id="err_div"></div></div>
|
||||||
|
<nav id="tabbar_injectparent">
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#" onclick="showSettings()">settings</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
</nav>
|
||||||
<main>
|
<main id="mainarea_injectparent">
|
||||||
<button id="connectbtn" onclick="connect()">Connect</button>
|
|
||||||
<div id="tabbar"></div>
|
|
||||||
<div id="tabcontent"></div>
|
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
<script src="./main.js" type="application/javascript"></script>
|
<script src="./main.js" type="application/javascript"></script>
|
||||||
|
|
22
dist/style.css
vendored
22
dist/style.css
vendored
|
@ -42,4 +42,26 @@ input {
|
||||||
|
|
||||||
.err {
|
.err {
|
||||||
color: var(--err_color);
|
color: var(--err_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li {
|
||||||
|
display: inline;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li a {
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: solid 1px var(--bg_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabbar_current {
|
||||||
|
border-bottom: solid 1px var(--main_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 2em;
|
||||||
}
|
}
|
|
@ -1,14 +1,15 @@
|
||||||
import {Message, Author} from "./message"
|
import {Message, Author} from "./message"
|
||||||
export class AdapterData {
|
export class AdapterData {
|
||||||
public protocol: string;
|
public protocol: string;
|
||||||
public directMessages Map<string, Message>();
|
public directMessages: Map<string, Message>;
|
||||||
public messages: Map<string, Message>();
|
public messages: Map<string, Message>;
|
||||||
public profileCache: Map<string, Author>;
|
public profileCache: Map<string, Author>;
|
||||||
|
|
||||||
constructor(protocol: string) {
|
constructor(protocol: string) {
|
||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
this.messages = [];
|
this.messages = new Map<string, Message>();
|
||||||
this.profileCache = [];
|
this.directMessages = new Map<string, Message>();
|
||||||
|
this.profileCache = new Map<string, Author>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
239
ts/index.ts
239
ts/index.ts
|
@ -1,29 +1,31 @@
|
||||||
import {Adapter} from "./adapter";
|
import {AdapterState, AdapterData} from "./adapter";
|
||||||
import {Message, Attachment} from "./message"
|
import {Message, Attachment, Author} from "./message"
|
||||||
import util from "./util"
|
import util from "./util"
|
||||||
|
import { TabBarElement } from "./tabbar-element"
|
||||||
|
import { MessageElement } from "./message-element"
|
||||||
|
import { SettingsElement } from "./settings-element"
|
||||||
|
|
||||||
var $ = util.$
|
var $ = util.$
|
||||||
var _ = util._
|
var _ = util._
|
||||||
|
|
||||||
function main():void {
|
function main() {
|
||||||
const settings = _("settings", JSON.parse(localStorage.getItem("settings") ?? "{}"));
|
const settings = _("settings", JSON.parse(localStorage.getItem("settings") ?? "{}"));
|
||||||
const adapters = _("adapters", []);
|
|
||||||
|
|
||||||
if (settings != null) {
|
customElements.define("underbbs-tabbar", TabBarElement);
|
||||||
for (let s of settings.adapters ?? []) {
|
customElements.define("underbbs-message", MessageElement);
|
||||||
|
customElements.define("underbbs-settings", SettingsElement);
|
||||||
}
|
|
||||||
if (adapters.length > 0) {
|
tabbarInit(settings.adapters?.map((a:any)=>a.nickname) ?? []);
|
||||||
_("currentAdapter", adapters[0].nickname);
|
|
||||||
// update tabbar and tabcontent with first adapter
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("no settings exist for this client");
|
|
||||||
_("settings", { adapters: [] });
|
|
||||||
showSettings();
|
|
||||||
}
|
|
||||||
registerServiceWorker();
|
registerServiceWorker();
|
||||||
};
|
}
|
||||||
|
|
||||||
|
function tabbarInit(adapters: string[]) {
|
||||||
|
const nav = $("tabbar_injectparent");
|
||||||
|
if (nav) {
|
||||||
|
nav.innerHTML = `<underbbs-tabbar data-adapters="${adapters.join(",")}" data-currentadapter=""></underbbs-tabbar>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function registerServiceWorker() {
|
async function registerServiceWorker() {
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
|
@ -44,207 +46,6 @@ async function registerServiceWorker() {
|
||||||
const registration = await navigator.serviceWorker.ready;
|
const registration = await navigator.serviceWorker.ready;
|
||||||
(registration as any).sync.register("testdata").then((r:any)=>{console.log("but i will see this!")});
|
(registration as any).sync.register("testdata").then((r:any)=>{console.log("but i will see this!")});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function showSettings():void {
|
|
||||||
// tab bar hidden
|
|
||||||
const tabbar = $("tabbar");
|
|
||||||
if (tabbar) {
|
|
||||||
tabbar.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
// tabcontent to show settings ui
|
|
||||||
const tabcontent = $("tabcontent");
|
|
||||||
const adapters = _("adapters") as Adapter[] ?? [];
|
|
||||||
|
|
||||||
if (tabcontent) {
|
|
||||||
let html = "<p>this is our settings dialogue</p>";
|
|
||||||
html += "<button onclick='addAdapter()'>New</button>";
|
|
||||||
html += adapters.reduce((self: string, a: Adapter) => {
|
|
||||||
self += `<li><a href='#' onclick='editAdapter(${a.nickname})'>${a.nickname}</a></li>`
|
|
||||||
return self;
|
|
||||||
}, "<ul id='settings_adapterlist'>");
|
|
||||||
html += "</ul>";
|
|
||||||
html += "<button onclick='saveSettings()'>save</button>";
|
|
||||||
tabcontent.innerHTML = html;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAdapter(): void {
|
|
||||||
const tabcontent = $("tabcontent");
|
|
||||||
if (tabcontent) {
|
|
||||||
// dropdown for protocol
|
|
||||||
let html = "<select id='settings_newadapter_protocolselect' onchange='fillAdapterProtocolOptions()'>";
|
|
||||||
html += [ "nostr", "mastodon", "misskey" ].reduce((self, p)=>{
|
|
||||||
self += `<option value='${p}'>${p}</option>`;
|
|
||||||
return self;
|
|
||||||
}, "");
|
|
||||||
html += "</select>";
|
|
||||||
|
|
||||||
// nostr is the first protocol, so show its options by default
|
|
||||||
html += "<div id='settings_newadapter_protocoloptions'>";
|
|
||||||
html += " <label>nickname<input id='settings_newadapter_nickname'/></label>";
|
|
||||||
html += " <label>privkey<input id='settings_newadapter_nostr_privkey'/></label>";
|
|
||||||
html += " <label>default relays<input id='settings_newadapter_nostr_default_relays'/></label>";
|
|
||||||
html += "</div>";
|
|
||||||
|
|
||||||
html += "<button onclick='saveAdapter()'>Add</button>";
|
|
||||||
html += "<button onclick='showSettings()'>Back</button>";
|
|
||||||
|
|
||||||
tabcontent.innerHTML = html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillAdapterProtocolOptions(): void {
|
|
||||||
const proto = $("settings_newadapter_protocolselect") as HTMLSelectElement;
|
|
||||||
|
|
||||||
let html = "";
|
|
||||||
|
|
||||||
switch(proto?.options[proto.selectedIndex].value) {
|
|
||||||
case "nostr":
|
|
||||||
html += " <label>nickname<input id='settings_newadapter_nickname'/></label>";
|
|
||||||
html += " <label>privkey<input id='settings_newadapter_nostr_privkey'/></label>";
|
|
||||||
html += " <label>default relays<input id='settings_newadapter_nostr_default_relays'/></label>";
|
|
||||||
break;
|
|
||||||
case "mastodon":
|
|
||||||
case "misskey":
|
|
||||||
html += " <label>nickname<input id='settings_newadapter_nickname'/></label>";
|
|
||||||
html += " <label>server<input id='settings_newadapter_masto_server'/></label>";
|
|
||||||
html += " <label>API key<input id='settings_newadapter_masto_apikey'/></label>";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const div = $("settings_newadapter_protocoloptions");
|
|
||||||
if (div) {
|
|
||||||
div.innerHTML = html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveSettings(): void {
|
|
||||||
const settings = _("settings");
|
|
||||||
if (settings) {
|
|
||||||
localStorage.setItem("settings", JSON.stringify(settings));
|
|
||||||
}
|
|
||||||
// tab bar hidden
|
|
||||||
const tabbar = $("tabbar");
|
|
||||||
if (tabbar) {
|
|
||||||
tabbar.style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
// tabcontent to show settings ui
|
|
||||||
const tabcontent = $("tabcontent");
|
|
||||||
if (tabcontent) {
|
|
||||||
tabcontent.innerHTML = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveAdapter(): void {
|
|
||||||
let self: any = {};
|
|
||||||
// get selected adapter protocol
|
|
||||||
const proto = $("settings_newadapter_protocolselect") as HTMLSelectElement;
|
|
||||||
console.log(proto.options[proto.selectedIndex]);
|
|
||||||
|
|
||||||
|
|
||||||
const nickname = ($("settings_newadapter_nickname") as HTMLInputElement)?.value ?? "" ;
|
|
||||||
|
|
||||||
// switch protocol
|
|
||||||
switch (proto.options[proto.selectedIndex].value) {
|
|
||||||
case "nostr":
|
|
||||||
const privkey = ($("settings_newadapter_nostr_privkey") as HTMLInputElement)?.value ?? "";
|
|
||||||
const relays = ($("settings_newadapter_nostr_default_relays") as HTMLInputElement)?.value ?? "";
|
|
||||||
self = { nickname: nickname, protocol: "nostr", privkey: privkey, relays: relays.split(",").map(r=>r.trim()) };
|
|
||||||
break;
|
|
||||||
case "mastodon":
|
|
||||||
case "misskey":
|
|
||||||
const server = ($("settings_newadapter_masto_server") as HTMLInputElement)?.value ?? "";
|
|
||||||
const apiKey = ($("settings_newadapter_masto_apikey") as HTMLInputElement)?.value ?? "";
|
|
||||||
self = { nickname: nickname, protocol: proto.options[proto.selectedIndex].value, server: server, apiKey: apiKey };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const settings = _("settings");
|
|
||||||
if (settings) {
|
|
||||||
if (!settings.adapters) {
|
|
||||||
settings.adapters = [];
|
|
||||||
}
|
|
||||||
settings.adapters.push(self);
|
|
||||||
|
|
||||||
localStorage.setItem("settings", JSON.stringify(settings));
|
|
||||||
showSettings();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let _conn: WebSocket | null = null;
|
|
||||||
|
|
||||||
async function authorizedFetch(method: string, uri: string, body: any): Promise<Response> {
|
|
||||||
const headers = new Headers()
|
|
||||||
headers.set('Authorization', 'Bearer ' + _("skey"))
|
|
||||||
return await fetch(uri, {
|
|
||||||
method: method,
|
|
||||||
headers: headers,
|
|
||||||
body: body,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function connect() {
|
|
||||||
|
|
||||||
var datastore: AdapterState = {}
|
|
||||||
datastore = _("datastore", datastore);
|
|
||||||
|
|
||||||
const wsProto = location.protocol == "https:" ? "wss" : "ws";
|
|
||||||
_conn = new WebSocket(`${wsProto}://${location.host}/subscribe`, "underbbs");
|
|
||||||
_conn.addEventListener("open", (e: any) => {
|
|
||||||
console.log("websocket connection opened");
|
|
||||||
console.log(JSON.stringify(e));
|
|
||||||
});
|
|
||||||
_conn.addEventListener("message", (e: any) => {
|
|
||||||
|
|
||||||
// debugging
|
|
||||||
console.log(e)
|
|
||||||
|
|
||||||
// now we'll figure out what to do with it
|
|
||||||
const data = JSON.parse(e.data);
|
|
||||||
if (data.key) {
|
|
||||||
_("skey", data.key)
|
|
||||||
authorizedFetch("POST", "/api/adapters", JSON.stringify(_("settings").adapters))
|
|
||||||
} else {
|
|
||||||
if (!datastore[data.adapter]) {
|
|
||||||
datastore[data.adapter] = new AdapterData(data.protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeswitch on the incoming data type and fill the memory
|
|
||||||
switch (data.type) {
|
|
||||||
case "message":
|
|
||||||
datastore[data.adapter].messages[data.id] = <Message>data;
|
|
||||||
break;
|
|
||||||
case "author":
|
|
||||||
datastore[data.adapter].profileCache[data.id] = <Author>data;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// if the adapter is active, inject the web components
|
|
||||||
// FOR HOTFETCHED DATA:
|
|
||||||
// before fetching, we can set properties on the DOM,
|
|
||||||
// so when those data return to us we know where to inject components!
|
|
||||||
if (_("currentAdapter") == data.adapter) {
|
|
||||||
// dive in and insert that shit in the dom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_conn.addEventListener("error", (e: any) => {
|
|
||||||
console.log("websocket connection error");
|
|
||||||
console.log(JSON.stringify(e));
|
|
||||||
});
|
|
||||||
_("websocket", _conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
_("addAdapter", addAdapter);
|
|
||||||
_("saveAdapter", saveAdapter);
|
|
||||||
_("fillAdapterProtocolOptions", fillAdapterProtocolOptions);
|
|
||||||
_("showSettings", showSettings);
|
|
||||||
_("saveSettings", saveSettings);
|
|
||||||
_("connect", connect);
|
|
||||||
main();
|
main();
|
||||||
|
|
160
ts/settings-element.ts
Normal file
160
ts/settings-element.ts
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
import util from "./util"
|
||||||
|
import websocket from "./websocket"
|
||||||
|
|
||||||
|
var $ = util.$
|
||||||
|
var _ = util._
|
||||||
|
|
||||||
|
export class SettingsElement extends HTMLElement {
|
||||||
|
static observedAttributes = [ "data-adapters" ]
|
||||||
|
|
||||||
|
private _adapters: string[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.attributeChangedCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback() {
|
||||||
|
this._adapters = this.getAttribute("data-adapters")?.split(",") ?? [];
|
||||||
|
this.showSettings(this)();
|
||||||
|
}
|
||||||
|
|
||||||
|
showSettings(self: SettingsElement): ()=>void {
|
||||||
|
return ()=>{
|
||||||
|
let html = "";
|
||||||
|
html += "<button id='settings_adapter_create_btn'>New</button>";
|
||||||
|
html += self._adapters.reduce((self: string, a: string) => {
|
||||||
|
self += `<li><a id='settings_adapter_edit_${a}' href='#editadapter'>${a}</a></li>`
|
||||||
|
return self;
|
||||||
|
}, "<ul id='settings_adapterlist'>");
|
||||||
|
html += "</ul>";
|
||||||
|
html += "<button id='settings_save_btn'>save</button>";
|
||||||
|
self.innerHTML = html;
|
||||||
|
|
||||||
|
let create = $("settings_adapter_create_btn");
|
||||||
|
if (create) {
|
||||||
|
create.addEventListener("click", self.showCreateAdapter(self), false);
|
||||||
|
}
|
||||||
|
for (let a of this._adapters) {
|
||||||
|
let edit = $(`settings_adapter_edit_${a}`);
|
||||||
|
if (edit) {
|
||||||
|
edit.addEventListener("click", self.showEditAdapterFunc(a, self), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let connect = $("settings_connect_btn");
|
||||||
|
if (connect) {
|
||||||
|
connect.addEventListener("click", websocket.connect, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showCreateAdapter(self: SettingsElement): ()=>void {
|
||||||
|
return ()=>{
|
||||||
|
// dropdown for protocol
|
||||||
|
let html = "<select id='settings_newadapter_protocolselect'>";
|
||||||
|
html += [ "nostr", "mastodon", "misskey" ].reduce((self, p)=>{
|
||||||
|
self += `<option value='${p}'>${p}</option>`;
|
||||||
|
return self;
|
||||||
|
}, "");
|
||||||
|
html += "</select>";
|
||||||
|
|
||||||
|
// nostr is the first protocol, so show its options by default
|
||||||
|
html += "<div id='settings_newadapter_protocoloptions'>";
|
||||||
|
html += " <label>nickname<input id='settings_newadapter_nickname'/></label>";
|
||||||
|
html += " <label>privkey<input id='settings_newadapter_nostr_privkey'/></label>";
|
||||||
|
html += " <label>default relays<input id='settings_newadapter_nostr_default_relays'/></label>";
|
||||||
|
html += "</div>";
|
||||||
|
|
||||||
|
html += "<button id='settings_adapter_create_save_btn'>add</button>";
|
||||||
|
html += "<button id='settings_adapter_create_back_btn'>back</button>";
|
||||||
|
|
||||||
|
self.innerHTML = html;
|
||||||
|
|
||||||
|
let protocolSelect = $("settings_newadapter_protocolselect");
|
||||||
|
if (protocolSelect) {
|
||||||
|
protocolSelect.addEventListener("change", self.fillAdapterProtocolOptions, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let save = $("settings_adapter_create_save_btn");
|
||||||
|
if (save) {
|
||||||
|
save.addEventListener("click", self.saveAdapter(self), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let back = $("settings_adapter_create_back_btn");
|
||||||
|
if (back) {
|
||||||
|
back.addEventListener("click", self.showSettings(self), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAdapter(self: SettingsElement): ()=>void {
|
||||||
|
return ()=>{
|
||||||
|
let adapterdata: any = {};
|
||||||
|
// get selected adapter protocol
|
||||||
|
const proto = $("settings_newadapter_protocolselect") as HTMLSelectElement;
|
||||||
|
console.log(proto.options[proto.selectedIndex]);
|
||||||
|
|
||||||
|
const nickname = ($("settings_newadapter_nickname") as HTMLInputElement)?.value ?? "" ;
|
||||||
|
|
||||||
|
// switch protocol
|
||||||
|
switch (proto.options[proto.selectedIndex].value) {
|
||||||
|
case "nostr":
|
||||||
|
const privkey = ($("settings_newadapter_nostr_privkey") as HTMLInputElement)?.value ?? "";
|
||||||
|
const relays = ($("settings_newadapter_nostr_default_relays") as HTMLInputElement)?.value ?? "";
|
||||||
|
adapterdata = { nickname: nickname, protocol: "nostr", privkey: privkey, relays: relays.split(",").map(r=>r.trim()) };
|
||||||
|
break;
|
||||||
|
case "mastodon":
|
||||||
|
case "misskey":
|
||||||
|
const server = ($("settings_newadapter_masto_server") as HTMLInputElement)?.value ?? "";
|
||||||
|
const apiKey = ($("settings_newadapter_masto_apikey") as HTMLInputElement)?.value ?? "";
|
||||||
|
adapterdata = { nickname: nickname, protocol: proto.options[proto.selectedIndex].value, server: server, apiKey: apiKey };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const settings = _("settings");
|
||||||
|
if (settings) {
|
||||||
|
if (!settings.adapters) {
|
||||||
|
settings.adapters = [];
|
||||||
|
}
|
||||||
|
settings.adapters.push(adapterdata);
|
||||||
|
self._adapters.push(adapterdata.nickname);
|
||||||
|
localStorage.setItem("settings", JSON.stringify(settings));
|
||||||
|
|
||||||
|
self.setAttribute("adapters", self._adapters.join(","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fillAdapterProtocolOptions() {
|
||||||
|
const proto = $("settings_newadapter_protocolselect") as HTMLSelectElement;
|
||||||
|
|
||||||
|
let html = "";
|
||||||
|
|
||||||
|
switch(proto?.options[proto.selectedIndex].value) {
|
||||||
|
case "nostr":
|
||||||
|
html += " <label>nickname<input id='settings_newadapter_nickname'/></label>";
|
||||||
|
html += " <label>privkey<input id='settings_newadapter_nostr_privkey'/></label>";
|
||||||
|
html += " <label>default relays<input id='settings_newadapter_nostr_default_relays'/></label>";
|
||||||
|
break;
|
||||||
|
case "mastodon":
|
||||||
|
case "misskey":
|
||||||
|
html += " <label>nickname<input id='settings_newadapter_nickname'/></label>";
|
||||||
|
html += " <label>server<input id='settings_newadapter_masto_server'/></label>";
|
||||||
|
html += " <label>API key<input id='settings_newadapter_masto_apikey'/></label>";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const div = $("settings_newadapter_protocoloptions");
|
||||||
|
if (div) {
|
||||||
|
div.innerHTML = html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
showEditAdapterFunc(adapter: string, self: SettingsElement): ()=>void {
|
||||||
|
// this UI has to be able to edit an exiting adapter or delete it
|
||||||
|
return ()=>{};
|
||||||
|
}
|
||||||
|
}
|
84
ts/tabbar-element.ts
Normal file
84
ts/tabbar-element.ts
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import util from "./util"
|
||||||
|
var _ = util._
|
||||||
|
var $ = util.$
|
||||||
|
|
||||||
|
export class TabBarElement extends HTMLElement {
|
||||||
|
static observedAttributes = [ "data-adapters", "data-currentadapter" ]
|
||||||
|
|
||||||
|
private _adapters: string[] | null = null;
|
||||||
|
private _currentAdapter: string | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.attributeChangedCallback();
|
||||||
|
|
||||||
|
if (this._currentAdapter) {
|
||||||
|
this.showAdapterFunc(this, this._currentAdapter)();
|
||||||
|
} else {
|
||||||
|
this.showSettings(this)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback() {
|
||||||
|
let html = "<ul><li><a id='tabbar_settings' href='#settings'>settings</a></li>";
|
||||||
|
if (this.getAttribute("data-adapters") == "") {
|
||||||
|
this._adapters = [];
|
||||||
|
} else {
|
||||||
|
this._adapters = this.getAttribute("data-adapters")?.split(",") ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this._currentAdapter = this.getAttribute("data-currentadapter");
|
||||||
|
if (this._currentAdapter == "") {
|
||||||
|
this._currentAdapter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
html = this._adapters.reduce((self: string, a: string)=>{
|
||||||
|
self += `<li><a id="tabbar_${a}" href="#${a}">${a}</a></li>`;
|
||||||
|
return self;
|
||||||
|
}, html);
|
||||||
|
html += "</ul>";
|
||||||
|
|
||||||
|
this.innerHTML = html;
|
||||||
|
// now we can query the child elements and add click handlers to them
|
||||||
|
var s = $("tabbar_settings");
|
||||||
|
if (s) {
|
||||||
|
s.addEventListener("click", this.showSettings(this), false);
|
||||||
|
if (!this._currentAdapter) {
|
||||||
|
s.classList.add("tabbar_current");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i of this._adapters) {
|
||||||
|
var a = $(`tabbar_${i}`);
|
||||||
|
if (a) {
|
||||||
|
a.addEventListener("click", this.showAdapterFunc(this, i), false);
|
||||||
|
if (this._currentAdapter == i) {
|
||||||
|
a.classList.add("tabbar_current");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
showSettings(self: TabBarElement): ()=>void {
|
||||||
|
return () => {
|
||||||
|
let x = $("mainarea_injectparent");
|
||||||
|
if (x) {
|
||||||
|
x.innerHTML = `<underbbs-settings data-adapters=${self._adapters?.join(",") ?? []}></underbbs-settings>`;
|
||||||
|
self.setAttribute("data-currentadapter", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showAdapterFunc(self: TabBarElement, adapter: string): ()=>void {
|
||||||
|
return ()=>{
|
||||||
|
let x = $("mainarea_injectparent");
|
||||||
|
if (x) {
|
||||||
|
x.innerHTML = `<underbbs-adapter data-name="${adapter}"></underbbs-adapter>`;
|
||||||
|
self.setAttribute("data-currentadapter", adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
ts/util.ts
12
ts/util.ts
|
@ -11,4 +11,14 @@ function $(id: string): HTMLElement | null {
|
||||||
return document.getElementById(id);
|
return document.getElementById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { _, $ }
|
async function authorizedFetch(method: string, uri: string, body: any): Promise<Response> {
|
||||||
|
const headers = new Headers()
|
||||||
|
headers.set('Authorization', 'Bearer ' + _("skey"))
|
||||||
|
return await fetch(uri, {
|
||||||
|
method: method,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { _, $, authorizedFetch }
|
|
@ -0,0 +1,61 @@
|
||||||
|
import util from "./util"
|
||||||
|
import {AdapterState, AdapterData} from "./adapter";
|
||||||
|
import {Message, Attachment, Author} from "./message"
|
||||||
|
|
||||||
|
var $ = util.$
|
||||||
|
var _ = util._
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
|
||||||
|
var datastore: AdapterState = {}
|
||||||
|
datastore = _("datastore", datastore);
|
||||||
|
|
||||||
|
const wsProto = location.protocol == "https:" ? "wss" : "ws";
|
||||||
|
const _conn = new WebSocket(`${wsProto}://${location.host}/subscribe`, "underbbs");
|
||||||
|
_conn.addEventListener("open", (e: any) => {
|
||||||
|
console.log("websocket connection opened");
|
||||||
|
console.log(JSON.stringify(e));
|
||||||
|
});
|
||||||
|
_conn.addEventListener("message", (e: any) => {
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
console.log(e)
|
||||||
|
|
||||||
|
// now we'll figure out what to do with it
|
||||||
|
const data = JSON.parse(e.data);
|
||||||
|
if (data.key) {
|
||||||
|
_("skey", data.key)
|
||||||
|
util.authorizedFetch("POST", "/api/adapters", JSON.stringify(_("settings").adapters))
|
||||||
|
} else {
|
||||||
|
if (!datastore[data.adapter]) {
|
||||||
|
datastore[data.adapter] = new AdapterData(data.protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeswitch on the incoming data type and fill the memory
|
||||||
|
switch (data.type) {
|
||||||
|
case "message":
|
||||||
|
datastore[data.adapter].messages.set(data.id, <Message>data);
|
||||||
|
break;
|
||||||
|
case "author":
|
||||||
|
datastore[data.adapter].profileCache.set(data.id, <Author>data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// if the adapter is active, inject the web components
|
||||||
|
// FOR HOTFETCHED DATA:
|
||||||
|
// before fetching, we can set properties on the DOM,
|
||||||
|
// so when those data return to us we know where to inject components!
|
||||||
|
if (_("currentAdapter") == data.adapter) {
|
||||||
|
// dive in and insert that shit in the dom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_conn.addEventListener("error", (e: any) => {
|
||||||
|
console.log("websocket connection error");
|
||||||
|
console.log(JSON.stringify(e));
|
||||||
|
});
|
||||||
|
_("websocket", _conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { connect }
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es6",
|
||||||
"lib": [ "es2022", "webworker", "dom" ],
|
"lib": [ "es2022", "webworker", "dom" ],
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"module": "preserve",
|
"module": "preserve",
|
||||||
|
|
Loading…
Reference in a new issue