import util from "./util" import { Message, Author } from "./message" import { MessageThread } from "./thread" var _ = util._ var $ = util.$ export class AdapterElement extends HTMLElement { static observedAttributes = [ "data-latest", "data-view", "data-viewing" ] private _latest: string = "" ; private _view: string = ""; private _name: string = "" private _viewing: string = ""; // TODO: use visibility of the thread to organize into DMs and public threads private _threads: MessageThread[] = []; private _orphans: Message[] = []; constructor() { super(); } connectedCallback() { const name = this.getAttribute("data-name"); this._name = name ?? ""; this._view = ""; this.buildThreads(); this.setAttribute("data-view", "index"); } attributeChangedCallback() { // set the viewing subject if it's changed const viewing = this.getAttribute("data-viewing"); if (this._viewing != viewing && viewing != null) { this._viewing = viewing; // if the viewing subject changed (not to nothing), unset the view // this will force it to refresh if (this._viewing) { this._view = ""; } } // initialize the view if it's changed const view = this.getAttribute("data-view"); if (this._view != view ?? "index") { this._view = view ?? "index"; switch (this._view) { case "index": this.setIdxView(); this.populateIdxView(); break; case "thread": this.setThreadView(); this.populateThreadView(); break; case "profile": this.setProfileView(); this.populateProfileView(); break; } } // if latest changed, check if it's a message const latest = this.getAttribute("latest"); if (latest ?? "" != this._latest) { this._latest = latest ?? ""; let datastore = _("datastore"); console.log(datastore); const latestMsg = datastore.messages.get(this._latest); if (latestMsg) { const rootId = this.placeMsg(this._latest); // if rootId is null, this is an orphan and we don't need to actually do any updates yet if (rootId) { switch (this._view) { case "index": this.updateIdxView(this._latest, rootId); break; case "thread": // if the the message is part of this thread, update it case "profile": // if the message is from this user, show it in their profile break; } } } else { const latestAuthor = _("datastore")[this._name].profileCache.get(this._latest); switch (this._view) { case "index": case "thread": case "profile": break; } } // so, try to insert it into the threads // then, switch on view // if index, iterate through the topics and find the one to indicate new activity, // if thread, if any relatives are in this thread, insert message appropriately // if profile, if latest is this profile, update it } } setIdxView() { this.innerHTML = "" } setThreadView() { let html = `← return to index`; html += ""; this.innerHTML = html; } setProfileView() { let profile_bar = $("profile_bar"); if (profile_bar) { // clear any previous data } else { // insert the profileSidebar into the dom } } populateIdxView() { // skip dm list for now // public/unified list const pl = $("public_list"); if (pl) { let html = ""; for (const t of this._threads) { html +=`
  • `; } pl.innerHTML = html; } } updateIdxView(latest: string, rootId: string) { const existingThread = this.querySelector("underbbs-thread-summary[msg='${this._latest}']"); const thread = this._threads.find(t=>t.root.data.id == rootId); if (existingThread && thread) { existingThread.setAttribute("data-latest", `${thread.latest[Symbol.toPrimitive]("number")}`); existingThread.setAttribute("data-len", `${thread.messageCount}`); existingThread.setAttribute("data-new", "true"); } else { // unified/public list for now const pl = $("public_list"); if (pl && thread) { pl.prepend(`
  • `); } } } populateThreadView() { } populateProfileView() { } buildThreads() { const datastore = _("datastore")[this._name]; // make multiple passes over the store until every message is either // placed in a thread, or orphaned and waiting for its parent to be returned do{ for (let k of datastore.messages.keys()) { this.placeMsg(k); } } while (this._threads.reduce((sum: number, thread: MessageThread)=>{ return sum + thread.messageCount; }, 0) + this._orphans.length < datastore.messages.keys().length); } placeMsg(k: string): string | null { const msg = _("datastore")[this._name].messages.get(k); for (let t of this._threads) { // avoid processing nodes again on subsequent passes if (t.findNode(t.root, msg.id)) { return null; } if (msg.replyTo) { let x = t.addReply(msg.replyTo, msg); if (x) { // after adding, we try to adopt some orphans const orphanChildren = this._orphans.filter(m=>m.replyTo == k); for (let o of orphanChildren) { let adopted = this.placeMsg(o.id); if (adopted) { this._orphans.splice(this._orphans.indexOf(o), 1); } } return t.root.data.id; } } } // if we made it this far, this message doesn't go in any existing thread // if it doesn't have a parent, we can make a new thread with it if (!msg.replyTo) { this._threads.push(new MessageThread(msg)); return msg.id; } // then, we should check if its parent is an orphan const orphanedParent = this._orphans.find(o=>o.id == msg.replyTo); if (orphanedParent) { // then, try to place them both if (this.placeMsg(orphanedParent.id)) { this._orphans.splice(this._orphans.indexOf(orphanedParent), 1); return this.placeMsg(msg); } } // otherwise we can orphan it and try to fill it in later if (this._orphans.filter(o=>o.id == msg.id).length == 0) { this._orphans.push(msg); // TODO: request the parent's data } return null; } }