underbbs/ts/adapter-element.ts

209 lines
No EOL
6.6 KiB
TypeScript

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.buildThreads();
this.attributeChangedCallback();
}
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) {
console.log("view changed! let's go")
this._view = view ?? "";
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 ?? "";
console.log("about to update the index view");
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 = "<ul id='dm_list'></ul><ul id='public_list'></ul>"
}
setThreadView() {
let html = `<a href="#${this._name}">&larr; return to index</a>`;
html += "<ul id='msg_list'></ul>";
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) {
console.log(JSON.stringify(this._threads));
for (const t of this._threads) {
console.log(t.root.data.id);
pl.append(`<li><underbbs-thread-summary data-len="${t.messageCount}" data-adapter="${t.root.data.adapter}" data-msg="${t.root.data.id}" data-latest="${t.latest}" data-created="${t.created}" data-new=""></underbbs-thread-summary></li>`);
}
}
}
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(`<li><underbbs-thread-summary data-len="${thread.messageCount}" data-adapter="${thread.root.data.adapter}" data-msg="${thread.root.data.id}" data-latest="${thread.latest}" data-created="${thread.created}" data-new="true"></underbbs-thread-summary></li>`);
}
}
}
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);
if (msg.replyTo) {
for (let t of this._threads) {
// avoid processing nodes again on subsequent passes
if (t.findNode(t.root, msg.id)) {
return null;
}
let x = t.findNode(t.root, msg.replyTo);
if (x) {
t.addReply(msg.replyTo, msg);
// 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 (this._orphans.filter(o=>o.id == msg.id).length == 0) {
this._orphans.push(msg);
// TODO: request the parent's data
}
return null;
} else {
this._threads.push(new MessageThread(msg));
return k;
}
}
}