From 64d00083b7d95899343411105b192057ef1198e2 Mon Sep 17 00:00:00 2001 From: Iris Lightshard Date: Sun, 1 Dec 2024 14:08:02 -0700 Subject: [PATCH] redoing frontend stuff, we have a basic profile fetch working --- adapter/anonAp.go | 2 +- adapter/honk.go | 6 ++- frontend/dist/index.html | 9 ++-- frontend/ts/boost-tile-element.ts | 1 + frontend/ts/index.ts | 42 +++++----------- frontend/ts/profile-element.ts | 82 +++++++++++++++++++++++++++++++ frontend/ts/settings-element.ts | 13 ++++- frontend/ts/settings.ts | 4 ++ frontend/ts/websocket.ts | 24 +++++---- server/api.go | 2 + 10 files changed, 135 insertions(+), 50 deletions(-) create mode 100644 frontend/ts/profile-element.ts diff --git a/adapter/anonAp.go b/adapter/anonAp.go index c13f1b2..e3db6b3 100644 --- a/adapter/anonAp.go +++ b/adapter/anonAp.go @@ -137,7 +137,7 @@ func getBodyJson(res *http.Response) []byte { l := res.ContentLength // 4k is a reasonable max size if we get unknown length right? if l < 0 { - l = 4096 + l = 4096 } jsonData := make([]byte, l) res.Body.Read(jsonData) diff --git a/adapter/honk.go b/adapter/honk.go index 6b9bc90..34affd1 100644 --- a/adapter/honk.go +++ b/adapter/honk.go @@ -36,13 +36,14 @@ func (self *HonkAdapter) Name() string { } func (self *HonkAdapter) Init(settings Settings, data *chan SocketData) error { + self.data = data // separate name and server in handle parts := strings.Split(*settings.Handle, "@") self.username = parts[1] self.server = "https://" + parts[2] self.nickname = settings.Nickname - if settings.Password == nil { + if settings.Password == nil || *settings.Password == "" { // we're anonymous! return nil } @@ -71,10 +72,11 @@ func (self *HonkAdapter) Init(settings Settings, data *chan SocketData) error { func (self *HonkAdapter) Subscribe(string) []error { // similar to the misskey adapter, we will poll for new honks and send them on the channel as they come in + return nil } func (self *HonkAdapter) Fetch(etype string, ids []string) error { - // honk API is limited, we fall back to the anonymous adapter for fetch ops + // honk API is limited, we fall back to the anonymous adapter for fetch ops aaa := anonAPAdapter{} aaa.Init(self.data, self.server, "honk", self.nickname) return aaa.Fetch(etype, ids) diff --git a/frontend/dist/index.html b/frontend/dist/index.html index 4a67454..c5e39f8 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -2,7 +2,7 @@ - underBBS + underBBS test @@ -13,9 +13,10 @@ - -
+
+
settings
+
profile
+
honks
diff --git a/frontend/ts/boost-tile-element.ts b/frontend/ts/boost-tile-element.ts index 8fe0da1..29680ae 100644 --- a/frontend/ts/boost-tile-element.ts +++ b/frontend/ts/boost-tile-element.ts @@ -3,6 +3,7 @@ export class BoostTileElement extends HTMLElement { static observedAttributes = [ "data-boostid", "data-msgid", "data-author", "data-booster" ]; constructor() { + super(); this.innerHTML = "
"; } diff --git a/frontend/ts/index.ts b/frontend/ts/index.ts index ea4f98b..d11a018 100644 --- a/frontend/ts/index.ts +++ b/frontend/ts/index.ts @@ -7,9 +7,11 @@ import { MessageElement } from "./message-element" import { SettingsElement } from "./settings-element" import { AdapterElement } from "./adapter-element" import { ThreadSummaryElement } from "./thread-summary-element" +import { ProfileElement } from "./profile-element" +import {DatagramSocket} from "./websocket" function main() { - const saveData = localStorage.getItem("settings"); + const saveData = localStorage.getItem("underbbs_settings"); Settings._instance = saveData ? JSON.parse(saveData) : new Settings(); customElements.define("underbbs-tabbar", TabBarElement); @@ -17,39 +19,17 @@ function main() { customElements.define("underbbs-settings", SettingsElement); customElements.define("underbbs-adapter", AdapterElement); customElements.define("underbbs-thread-summary", ThreadSummaryElement); + customElements.define("underbbs-profile", ProfileElement); util._("closeErr", util.closeErr); - - tabbarInit(Settings._instance.adapters?.map(a=>a.nickname) ?? []); - - registerServiceWorker(); -} - -function tabbarInit(adapters: string[]) { - const nav = util.$("tabbar_injectparent"); - if (nav) { - nav.innerHTML = ``; + let settingsParent = util.$("settings_parent"); + if (settingsParent) { + settingsParent.innerHTML = `` } -} - -async function registerServiceWorker() { - if ("serviceWorker" in navigator) { - try { - const registration = await navigator.serviceWorker.register("/serviceWorker.js", { - scope: "/", - }); - if (registration.installing) { - console.log("Service worker installing"); - } else if (registration.waiting) { - console.log("Service worker installed"); - } else if (registration.active) { - console.log("Service worker active"); - } - } catch (error) { - console.error(`Registration failed with ${error}`); - } - const registration = await navigator.serviceWorker.ready; - (registration as any).sync.register("testdata").then((r:any)=>{console.log("but i will see this!")}); + + let profileParent = util.$("profile_parent"); + if (profileParent) { + profileParent.innerHTML = "" } } diff --git a/frontend/ts/profile-element.ts b/frontend/ts/profile-element.ts new file mode 100644 index 0000000..0311c2d --- /dev/null +++ b/frontend/ts/profile-element.ts @@ -0,0 +1,82 @@ +import { Author } from "./message" +import util from "./util" +import { BatchTimer } from "./batch-timer" +import { AdapterState } from "./adapter" + +export class ProfileElement extends HTMLElement { + static observedAttributes = [ "data-latest", "data-adapter", "data-target" ] + + private _id: string | null = null; + private _adapter: string = ""; + + private _author: Author | null = null; + + private _authorTimer: BatchTimer | null = null; + + + constructor() { + super(); + this.innerHTML = "
" + + } + + connectedCallback() { + this._id = this.getAttribute("data-target"); + this._adapter = this.getAttribute("data-adapter") ?? ""; + const gateway = this.getAttribute("data-gateway") ?? ""; + this._authorTimer = new BatchTimer((ids: string[])=>{ + let url = `${gateway}/api/adapters/${this._adapter}/fetch?entity_type=author`; + for (let id of ids) { + url += `&entity_id=${id}`; + } + util.authorizedFetch("GET", url, null) + }); + } + + attributeChangedCallback(attr: string, prev: string, next: string) { + + switch (attr) { + case "data-target": + if (!next) { + return + } + this._id = next; + if (this._authorTimer) { + + this._authorTimer.queue(next, 100); + } + break; + case "data-latest": + let datastore = AdapterState._instance.data.get(this._adapter); + if (!datastore) { + console.log("no data yet, wait for some to come in maybe..."); + return; + } + this._author = datastore.profileCache.get(next) ?? null; + console.log(this._author); + if (this._author == null) { + return; + } + if (this._author.id == next) { + let pfp = this.querySelector(".author_pfp"); + let handle = this.querySelector(".author_id"); + let prefName = this.querySelector(".author_name"); + let bio = this.querySelector(".author_description"); + + if (pfp) { + pfp.innerHTML = `` + } + if (handle) { + handle.innerHTML = `${this._author.id}`; + } + if (prefName) { + prefName.innerHTML = this._author.name; + } + if (bio) { + bio.innerHTML = this._author.profileData; + } + } + break; + } + } +} \ No newline at end of file diff --git a/frontend/ts/settings-element.ts b/frontend/ts/settings-element.ts index b30de5a..6d06a81 100644 --- a/frontend/ts/settings-element.ts +++ b/frontend/ts/settings-element.ts @@ -58,7 +58,7 @@ export class SettingsElement extends HTMLElement { return ()=>{ // dropdown for protocol let html = ""; html += " "; break; + case "honk": + html += " "; + html += " "; + html += " "; } const div = util.$("settings_newadapter_protocoloptions"); diff --git a/frontend/ts/settings.ts b/frontend/ts/settings.ts index 24a1aad..881c23b 100644 --- a/frontend/ts/settings.ts +++ b/frontend/ts/settings.ts @@ -10,6 +10,10 @@ export class AdapterConfig { // nostr public privkey: string | null = null; public relays: string[] | null = null; + + // honk + public handle: string | null = null; + public password: string | null = null; } export class Settings { diff --git a/frontend/ts/websocket.ts b/frontend/ts/websocket.ts index ef01f2b..8dbd829 100644 --- a/frontend/ts/websocket.ts +++ b/frontend/ts/websocket.ts @@ -22,10 +22,12 @@ export class DatagramSocket { util.authorizedFetch("POST", "/api/adapters", JSON.stringify(Settings._instance.adapters)) .then(r=> { if (r.ok) { - const tabbar = document.querySelector("underbbs-tabbar"); - if (tabbar) { - tabbar.setAttribute("data-adapters", Settings._instance.adapters.map(a=>a.nickname).join(",")); - } + // iterate through any components which might want to fetch data + const profiles = document.querySelectorAll("underbbs-profile"); + profiles.forEach(p=>{ + const target = p.getAttribute("data-target"); + p.setAttribute("data-target", target ?? ""); + }); } }) .catch(e => { @@ -36,7 +38,8 @@ export class DatagramSocket { if (!store) { AdapterState._instance.data.set(data.adapter, new AdapterData(data.protocol)); store = AdapterState._instance.data.get(data.adapter); - } else { + } + if (store) { // typeswitch on the incoming data type and fill the memory switch (data.type) { case "message": @@ -49,11 +52,12 @@ export class DatagramSocket { break; } } - // if the adapter is active signal it that there's new data - let adapter = util.$(`adapter_${data.adapter}`); - if (adapter) { - adapter.setAttribute("data-latest", data.id); - } + console.log(store); + // now search for any components with this adapter and target + let targets = document.querySelectorAll(`*[data-adapter="${data.adapter}"][data-target="${data.id}"]`); + targets.forEach(t=>{ + t.setAttribute("data-latest", data.id); + }); } } diff --git a/server/api.go b/server/api.go index b18e829..7660dbe 100644 --- a/server/api.go +++ b/server/api.go @@ -80,6 +80,8 @@ func apiConfigureAdapters(next http.Handler, subscribers map[*Subscriber][]adapt a = &adapter.MastoAdapter{} case "misskey": a = &adapter.MisskeyAdapter{} + case "honk": + a = &adapter.HonkAdapter{} default: break