merge author-messages and timeline components, start implementing navigator for viewing individual messages and authors
This commit is contained in:
parent
2ff69b9d38
commit
1806140f4b
10 changed files with 207 additions and 156 deletions
|
@ -225,11 +225,27 @@ func (self *anonAPAdapter) Fetch(etype string, ids []string) error {
|
||||||
if string([]byte{id[0]}) == "@" {
|
if string([]byte{id[0]}) == "@" {
|
||||||
id = id[1:]
|
id = id[1:]
|
||||||
}
|
}
|
||||||
res, err := http.Get(self.server + "/.well-known/webfinger?resource=acct:" + id)
|
reqHost := self.server
|
||||||
|
if strings.HasPrefix(id, "https://") || !strings.HasSuffix(id, strings.Split(self.server, "https://")[1]) {
|
||||||
|
if strings.Contains(id, "@") {
|
||||||
|
reqHost = "https://" + strings.Split(id, "@")[1]
|
||||||
|
id = strings.Split(id, "@")[0]
|
||||||
|
} else {
|
||||||
|
noScheme := strings.TrimPrefix(id, "https://")
|
||||||
|
domainOnly := strings.Split(noScheme, "/")[0]
|
||||||
|
reqHost = "https://" + domainOnly
|
||||||
|
idParts := strings.Split(id, "/")
|
||||||
|
id = idParts[len(idParts)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println(reqHost)
|
||||||
|
res, err := http.Get(reqHost + "/.well-known/webfinger?resource=acct:" + id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Printf("%d\n", res.StatusCode)
|
||||||
data := getBodyJson(res)
|
data := getBodyJson(res)
|
||||||
|
fmt.Println(string(data))
|
||||||
wf := webFinger{}
|
wf := webFinger{}
|
||||||
json.Unmarshal(data, &wf)
|
json.Unmarshal(data, &wf)
|
||||||
var profile string
|
var profile string
|
||||||
|
|
49
frontend/dist/style.css
vendored
49
frontend/dist/style.css
vendored
|
@ -1,11 +1,3 @@
|
||||||
:root {
|
|
||||||
--bg_color: #000000;
|
|
||||||
--fg_color: #ccc;
|
|
||||||
--main_color: #1f9b92;
|
|
||||||
--sub_color: #002b36;
|
|
||||||
--err_color: #DC143C;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -17,17 +9,6 @@
|
||||||
background: var(--bg_color);
|
background: var(--bg_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
* { scrollbar-color:var(--main_color) var(--sub_color); }
|
|
||||||
*::-webkit-scrollbar { width:6px;height:6px; }
|
|
||||||
*::-webkit-scrollbar-track { background: var(--sub_color);}
|
|
||||||
*::-webkit-scrollbar-thumb { background:var(--main_color);border-radius:0;border:none; }
|
|
||||||
*::-webkit-scrollbar-corner { background:var(--sub_color); }
|
|
||||||
*::selection { background-color:var(--main_color);color:var(--bg_color);text-decoration:none;text-shadow:none; }
|
|
||||||
|
|
||||||
body {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--main_color);
|
color: var(--main_color);
|
||||||
}
|
}
|
||||||
|
@ -44,27 +25,23 @@ input {
|
||||||
color: var(--err_color);
|
color: var(--err_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul li {
|
underbbs-message, underbbs-profile {
|
||||||
display: inline;
|
max-width: 70ch;
|
||||||
padding: 0.5em;
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
underbbs-message img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
underbbs-profile img {
|
||||||
|
max-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul li a {
|
underbbs-message .message_metadata span {
|
||||||
text-decoration: none;
|
display: block;
|
||||||
border-bottom: solid 1px var(--bg_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar_current {
|
|
||||||
border-bottom: solid 1px var(--main_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
padding: 2em;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
grid-template-rows: auto;
|
|
||||||
}
|
}
|
|
@ -1,92 +0,0 @@
|
||||||
import { Author, Message } from "./message"
|
|
||||||
import util from "./util"
|
|
||||||
import { BatchTimer } from "./batch-timer"
|
|
||||||
import { AdapterState } from "./adapter"
|
|
||||||
|
|
||||||
export class AuthorMessagesElement extends HTMLElement {
|
|
||||||
static observedAttributes = [ "data-latest", "data-adapter", "data-target" ];
|
|
||||||
|
|
||||||
private _id: string | null = null;
|
|
||||||
private _adapter: string = "";
|
|
||||||
|
|
||||||
private _interactable: boolean = false;
|
|
||||||
|
|
||||||
private _messages: Message[] = [];
|
|
||||||
private _byAuthorTimer: BatchTimer | null = null;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.innerHTML = `<ul class="messages_list"></ul>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
this._id = this.getAttribute("data-target");
|
|
||||||
this._adapter = this.getAttribute("data-adapter") ?? "";
|
|
||||||
const gateway = this.getAttribute("data-gateway") ?? "";
|
|
||||||
this._byAuthorTimer = new BatchTimer(gateway, this._adapter, "byAuthor");
|
|
||||||
this._interactable = this.getAttribute("data-interactable") != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
attributeChangedCallback(attr: string, prev: string, next: string) {
|
|
||||||
switch (attr) {
|
|
||||||
case "data-target":
|
|
||||||
if (!next) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this._id = next;
|
|
||||||
if (this._byAuthorTimer) {
|
|
||||||
this._byAuthorTimer.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;
|
|
||||||
}
|
|
||||||
let msg = datastore.messages.get(next);
|
|
||||||
if (msg) {
|
|
||||||
const existingIdx = this._messages.findIndex(m=>(m.renoteId ?? m.id) == (msg.renoteId ?? msg.id) && ((m.edited ?? m.created) <= (msg.edited ?? msg.created)));
|
|
||||||
|
|
||||||
// first we update the backing data store
|
|
||||||
if (existingIdx >= 0) {
|
|
||||||
this._messages[existingIdx] = msg;
|
|
||||||
} else if (!this._messages.some(m=>(m.renoteId ?? m.id) == (msg.renoteId ?? msg.id))) {
|
|
||||||
this._messages.push(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const ul = this.children[0];
|
|
||||||
if (ul) {
|
|
||||||
// first pass through the dom, try to update a message if it's there
|
|
||||||
for (let i = 0; i < ul.childElementCount; i++){
|
|
||||||
const id = ul.children[i]?.children[0]?.getAttribute("data-target");
|
|
||||||
const ogMsg = this._messages.find(m=>(m.renoteId ?? m.id) == id);
|
|
||||||
if (ogMsg && existingIdx >= 0) {
|
|
||||||
ul.children[i]?.children[0]?.setAttribute("data-latest", id ?? "");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we made it this far, let's create a node
|
|
||||||
const e = document.createElement("li");
|
|
||||||
e.innerHTML = `<underbbs-message data-adapter="${this._adapter}" data-target="${next}" ${this._interactable ? "data-interactable" : ""}></underbbs-message>`
|
|
||||||
// second pass, try to place it in reverse-chronological order
|
|
||||||
for (let i = 0; i < ul.childElementCount; i++){
|
|
||||||
const id = ul.children[i]?.children[0]?.getAttribute("data-target");
|
|
||||||
const ogMsg = this._messages.find(m=>(m.renoteId ?? m.id) == id);
|
|
||||||
if (ogMsg && (ogMsg.renoteTime ?? ogMsg.created) <= (msg.renoteTime ?? msg.created)) {
|
|
||||||
ul.insertBefore(e, ul.children[i])
|
|
||||||
e.children[0].setAttribute("data-latest", next);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// final pass, we must be the earliest child (or maybe the first one to be rendered)
|
|
||||||
ul.append(e);
|
|
||||||
e.children[0].setAttribute("data-latest", next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import util from './util'
|
import util from './util'
|
||||||
|
|
||||||
export class BatchTimer {
|
export class Fetcher {
|
||||||
private _batch: string[];
|
private _batch: string[];
|
||||||
private _timer: number;
|
private _timer: number;
|
||||||
private _reqFn: (id: string[])=>void;
|
private _reqFn: (id: string[])=>void;
|
|
@ -1,6 +1,6 @@
|
||||||
import util from "./util"
|
import util from "./util"
|
||||||
import { Message } from "./message"
|
import { Message } from "./message"
|
||||||
import { BatchTimer } from "./batch-timer"
|
import { Fetcher } from "./fetcher"
|
||||||
import { AdapterState } from "./adapter"
|
import { AdapterState } from "./adapter"
|
||||||
|
|
||||||
export class MessageElement extends HTMLElement {
|
export class MessageElement extends HTMLElement {
|
||||||
|
@ -12,8 +12,9 @@ export class MessageElement extends HTMLElement {
|
||||||
private _message: Message | null = null;
|
private _message: Message | null = null;
|
||||||
private _interactable: boolean = false;
|
private _interactable: boolean = false;
|
||||||
private _replyWith: string | null = null;
|
private _replyWith: string | null = null;
|
||||||
|
private _inspectWith: string | null = null;
|
||||||
|
|
||||||
private _messageTimer: BatchTimer | null = null;
|
private _msgFetcher: Fetcher | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -24,9 +25,10 @@ export class MessageElement extends HTMLElement {
|
||||||
this._id = this.getAttribute("data-target");
|
this._id = this.getAttribute("data-target");
|
||||||
this._adapter = this.getAttribute("data-adapter");
|
this._adapter = this.getAttribute("data-adapter");
|
||||||
const gateway = this.getAttribute("data-gateway") ?? "";
|
const gateway = this.getAttribute("data-gateway") ?? "";
|
||||||
this._messageTimer = new BatchTimer(gateway, this._adapter ?? "", "message");
|
this._msgFetcher = new Fetcher(gateway, this._adapter ?? "", "message");
|
||||||
this._interactable = this.getAttribute("data-interactable") != null;
|
this._interactable = this.getAttribute("data-interactable") != null;
|
||||||
this._replyWith = this.getAttribute("data-replywith");
|
this._replyWith = this.getAttribute("data-replywith");
|
||||||
|
this._inspectWith = this.getAttribute("data-inspectwith");
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeChangedCallback(attr: string, prev: string, next: string) {
|
attributeChangedCallback(attr: string, prev: string, next: string) {
|
||||||
|
@ -38,8 +40,8 @@ export class MessageElement extends HTMLElement {
|
||||||
this._id = next;
|
this._id = next;
|
||||||
this._message = null;
|
this._message = null;
|
||||||
this.innerHTML = `<div class="message_metadata"></div><div class="message_content"></div><div class="message_attachments"></div><div class="message_interactions"></div>`;
|
this.innerHTML = `<div class="message_metadata"></div><div class="message_content"></div><div class="message_attachments"></div><div class="message_interactions"></div>`;
|
||||||
if (this._messageTimer) {
|
if (this._msgFetcher) {
|
||||||
this._messageTimer.queue(next, 100);
|
this._msgFetcher.queue(next, 100);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "data-latest":
|
case "data-latest":
|
||||||
|
@ -49,7 +51,6 @@ export class MessageElement extends HTMLElement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let msg = datastore.messages.get(next);
|
let msg = datastore.messages.get(next);
|
||||||
console.log("MessageElement.attributeChangedCallback: " + JSON.stringify(msg));
|
|
||||||
if (msg) {
|
if (msg) {
|
||||||
this._message = msg;
|
this._message = msg;
|
||||||
const metadata = this.querySelector(".message_metadata");
|
const metadata = this.querySelector(".message_metadata");
|
||||||
|
@ -58,11 +59,29 @@ export class MessageElement extends HTMLElement {
|
||||||
const interactions = this.querySelector(".message_interactions");
|
const interactions = this.querySelector(".message_interactions");
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
if (this._message.renoteId) {
|
if (this._message.renoteId) {
|
||||||
metadata.innerHTML = `<span class="message_renoter">${this._message.renoter}</span><span class="message_renotetime">${new Date(this._message.renoteTime ?? 0)}</span>`
|
metadata.innerHTML = `<span class="message_renoter">${this._message.renoter} boosted</span><span class="message_renotetime">${new Date(this._message.renoteTime ?? 0)}</span>`
|
||||||
} else {
|
} else {
|
||||||
metadata.innerHTML = "";
|
metadata.innerHTML = "";
|
||||||
}
|
}
|
||||||
metadata.innerHTML += `<span class="message_author">${this._message.author}</span><span class="message_timestamp">${new Date(this._message.created)}</span><span class="message_visibility">${this._message.visibility}</span><span class="message_protocol">${this._message.protocol}</span>`
|
metadata.innerHTML += `<span class="message_author">${this._message.author}</span><span class="message_timestamp">${new Date(this._message.created)}</span><span class="message_url">${this._message.uri}</span>${this._message.replyTo ? "<span class='message_inreplyto'>reply to " + this._message.replyTo + "</span>" : ""}<span class="message_visibility">${this._message.visibility}</span><span class="message_protocol">${this._message.protocol}</span>`
|
||||||
|
|
||||||
|
const renoter = this.querySelector(".message_renoter");
|
||||||
|
const author = this.querySelector(".message_author");
|
||||||
|
const url = this.querySelector(".message_url");
|
||||||
|
const replyToUrl = this.querySelector(".message_inreplyto");
|
||||||
|
|
||||||
|
if (renoter) {
|
||||||
|
renoter.addEventListener("click", this.inspect(this._message.renoter ?? "", "author"));
|
||||||
|
}
|
||||||
|
if (author) {
|
||||||
|
author.addEventListener("click", this.inspect(this._message.author, "author"));
|
||||||
|
}
|
||||||
|
if (url) {
|
||||||
|
url.addEventListener("click", this.inspect(this._message.uri, "message"));
|
||||||
|
}
|
||||||
|
if (replyToUrl) {
|
||||||
|
replyToUrl.addEventListener("click", this.inspect(this._message.replyTo ?? "", "message"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (content) {
|
if (content) {
|
||||||
content.innerHTML = this._message.content;
|
content.innerHTML = this._message.content;
|
||||||
|
@ -110,7 +129,7 @@ export class MessageElement extends HTMLElement {
|
||||||
attachments.innerHTML = html;
|
attachments.innerHTML = html;
|
||||||
}
|
}
|
||||||
if (this._interactable && interactions) {
|
if (this._interactable && interactions) {
|
||||||
interactions.innerHTML = `<button class="message_reply">reply</button><button class="message_boost">boost</button><a target="_blank" href="${this._message.uri}">permalink</a>`
|
interactions.innerHTML = `<button class="message_reply">reply</button><button class="message_boost">boost</button>`
|
||||||
const replyBtn = this.querySelector(".message_reply");
|
const replyBtn = this.querySelector(".message_reply");
|
||||||
const boostBtn = this.querySelector(".message_boost");
|
const boostBtn = this.querySelector(".message_boost");
|
||||||
if (replyBtn) {
|
if (replyBtn) {
|
||||||
|
@ -139,4 +158,17 @@ export class MessageElement extends HTMLElement {
|
||||||
private boost() {
|
private boost() {
|
||||||
// use a Doer to boost
|
// use a Doer to boost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inspect(target: string, type: string): ()=>void {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
return ()=> {
|
||||||
|
const e = document.querySelector(`#${self._inspectWith}`);
|
||||||
|
if (e) {
|
||||||
|
e.setAttribute("data-" + type, target);
|
||||||
|
} else {
|
||||||
|
window.open(target, "_blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
108
frontend/ts/navigator.ts
Normal file
108
frontend/ts/navigator.ts
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import { AdapterState } from "./adapter"
|
||||||
|
|
||||||
|
class HistoryNode {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
prev: HistoryNode | null = null;
|
||||||
|
next: HistoryNode | null = null;
|
||||||
|
|
||||||
|
constructor(id: string, type: string) {
|
||||||
|
this.id = id;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NavigatorElement extends HTMLElement {
|
||||||
|
static observedAttributes = [ "data-author", "data-message" ];
|
||||||
|
|
||||||
|
private _adapter: string = "";
|
||||||
|
private _history: HistoryNode | null = null;
|
||||||
|
private _replyWith: string | null = null;
|
||||||
|
private _gateway: string = "";
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.innerHTML = `<nav><button class="nav_prev">←</button><button class="nav_next">→</button><button class="nav_clear">×</button></nav><div class="nav_container"></div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this._adapter = this.getAttribute("data-adapter") ?? "";
|
||||||
|
this._replyWith = this.getAttribute("data-replywith");
|
||||||
|
this._gateway = this.getAttribute("data-gateway") ?? "";
|
||||||
|
|
||||||
|
const prevBtn = this.querySelector(".nav_prev");
|
||||||
|
const nextBtn = this.querySelector(".nav_next");
|
||||||
|
const clearBtn = this.querySelector(".nav_clear");
|
||||||
|
|
||||||
|
if (prevBtn) {
|
||||||
|
prevBtn.addEventListener("click", this.goPrev);
|
||||||
|
}
|
||||||
|
if (nextBtn) {
|
||||||
|
nextBtn.addEventListener("click", this.goNext);
|
||||||
|
}
|
||||||
|
if (clearBtn) {
|
||||||
|
clearBtn.addEventListener("click", this.clear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(attr: string, prev: string, next: string) {
|
||||||
|
switch (attr) {
|
||||||
|
case "data-author":
|
||||||
|
case "data-message":
|
||||||
|
if (next == prev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._history && this._history.prev && this._history.prev.id == next) {
|
||||||
|
this._history = this._history.prev;
|
||||||
|
} else {
|
||||||
|
const h = this._history;
|
||||||
|
this._history = new HistoryNode(next, attr.slice(attr.indexOf("-") + 1));
|
||||||
|
this._history.prev = h;
|
||||||
|
}
|
||||||
|
const panel = this.querySelector(".nav_container");
|
||||||
|
const datastore = AdapterState._instance.data.get(this._adapter);
|
||||||
|
if (datastore && panel) {
|
||||||
|
switch (attr) {
|
||||||
|
case "data-author":
|
||||||
|
const author = datastore.profileCache.get(next);
|
||||||
|
panel.innerHTML = `<underbbs-profile data-gateway="${this._gateway}" data-adapter="${this._adapter}" data-target="${next}"></underbbs-profile><underbbs-timeline data-gateway="${this._gateway}" data-target="${next}" data-adapter="${this._adapter}" data-interactable data-mode="fetch" data-inspectwith="${this.getAttribute("id")??""}" data-replywith="${this._replyWith}"></underbbs-timeline>`
|
||||||
|
|
||||||
|
const profile = this.querySelector("underbbs-profile");
|
||||||
|
const tl = this.querySelector("underbbs-timeline");
|
||||||
|
if (profile && tl) {
|
||||||
|
if (!author) {
|
||||||
|
profile.setAttribute("data-target", next);
|
||||||
|
} else {
|
||||||
|
profile.setAttribute("data-latest", next);
|
||||||
|
}
|
||||||
|
tl.setAttribute("data-target", next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "data-message":
|
||||||
|
const msg = datastore.messages.get(next);
|
||||||
|
panel.innerHTML = `<underbbs-message data-gateway="${this._gateway}" data-adapter="${this._adapter}" data-target="${next}" data-interactable data-inspectwith="${this.getAttribute("id")??""}" data-replywith="${this._replyWith}"></underbbs-message>`
|
||||||
|
const e = this.querySelector("underbbs-message");
|
||||||
|
if (e) {
|
||||||
|
if (!msg) {
|
||||||
|
e.setAttribute("data-target", next);
|
||||||
|
} else {
|
||||||
|
e.setAttribute("data-latest", next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private goNext() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private goPrev() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private clear() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { Author } from "./message"
|
import { Author } from "./message"
|
||||||
import util from "./util"
|
import util from "./util"
|
||||||
import { BatchTimer } from "./batch-timer"
|
import { Fetcher } from "./fetcher"
|
||||||
import { AdapterState } from "./adapter"
|
import { AdapterState } from "./adapter"
|
||||||
|
|
||||||
export class ProfileElement extends HTMLElement {
|
export class ProfileElement extends HTMLElement {
|
||||||
|
@ -11,7 +11,7 @@ export class ProfileElement extends HTMLElement {
|
||||||
|
|
||||||
private _author: Author | null = null;
|
private _author: Author | null = null;
|
||||||
|
|
||||||
private _authorTimer: BatchTimer | null = null;
|
private _authorFetcher: Fetcher | null = null;
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -24,7 +24,7 @@ export class ProfileElement extends HTMLElement {
|
||||||
this._id = this.getAttribute("data-target");
|
this._id = this.getAttribute("data-target");
|
||||||
this._adapter = this.getAttribute("data-adapter") ?? "";
|
this._adapter = this.getAttribute("data-adapter") ?? "";
|
||||||
const gateway = this.getAttribute("data-gateway") ?? "";
|
const gateway = this.getAttribute("data-gateway") ?? "";
|
||||||
this._authorTimer = new BatchTimer(gateway, this._adapter, "author");
|
this._authorFetcher = new Fetcher(gateway, this._adapter, "author");
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeChangedCallback(attr: string, prev: string, next: string) {
|
attributeChangedCallback(attr: string, prev: string, next: string) {
|
||||||
|
@ -35,8 +35,8 @@ export class ProfileElement extends HTMLElement {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this._id = next;
|
this._id = next;
|
||||||
if (this._authorTimer) {
|
if (this._authorFetcher) {
|
||||||
this._authorTimer.queue(next, 100);
|
this._authorFetcher.queue(next, 100);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "data-latest":
|
case "data-latest":
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Author, Message } from "./message"
|
import { Author, Message } from "./message"
|
||||||
import util from "./util"
|
import util from "./util"
|
||||||
import { Subscriber } from "./subscriber"
|
import { Subscriber } from "./subscriber"
|
||||||
|
import { Fetcher } from "./fetcher"
|
||||||
import { AdapterState } from "./adapter"
|
import { AdapterState } from "./adapter"
|
||||||
|
|
||||||
export class TimelineElement extends HTMLElement {
|
export class TimelineElement extends HTMLElement {
|
||||||
|
@ -11,9 +12,12 @@ export class TimelineElement extends HTMLElement {
|
||||||
|
|
||||||
private _interactable: boolean = false;
|
private _interactable: boolean = false;
|
||||||
private _replyWith: string | null = null;
|
private _replyWith: string | null = null;
|
||||||
|
private _inspectWith: string | null = null;
|
||||||
|
private _mode: string = "subscribe";
|
||||||
|
|
||||||
private _messages: Message[] = [];
|
private _messages: Message[] = [];
|
||||||
private _subscriber: Subscriber | null = null;
|
private _subscriber: Subscriber | null = null;
|
||||||
|
private _byAuthorFetcher: Fetcher | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -25,8 +29,11 @@ export class TimelineElement extends HTMLElement {
|
||||||
this._adapter = this.getAttribute("data-adapter") ?? "";
|
this._adapter = this.getAttribute("data-adapter") ?? "";
|
||||||
const gateway = this.getAttribute("data-gateway") ?? "";
|
const gateway = this.getAttribute("data-gateway") ?? "";
|
||||||
this._subscriber = new Subscriber(gateway, this._adapter ?? "", this.getAttribute("id") ?? null);
|
this._subscriber = new Subscriber(gateway, this._adapter ?? "", this.getAttribute("id") ?? null);
|
||||||
|
this._byAuthorFetcher = new Fetcher(gateway, this._adapter, "byAuthor");
|
||||||
this._interactable = this.getAttribute("data-interactable") != null;
|
this._interactable = this.getAttribute("data-interactable") != null;
|
||||||
this._replyWith = this.getAttribute("data-replywith");
|
this._replyWith = this.getAttribute("data-replywith");
|
||||||
|
this._inspectWith = this.getAttribute("data-inspectwith");
|
||||||
|
this._mode = this.getAttribute("data-mode") ?? "subscribe";
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeChangedCallback(attr: string, prev: string, next: string) {
|
attributeChangedCallback(attr: string, prev: string, next: string) {
|
||||||
|
@ -38,8 +45,17 @@ export class TimelineElement extends HTMLElement {
|
||||||
this._timeline = next;
|
this._timeline = next;
|
||||||
this.innerHTML = `<ul class="messages_list"></ul>`;
|
this.innerHTML = `<ul class="messages_list"></ul>`;
|
||||||
this._messages = [];
|
this._messages = [];
|
||||||
if (this._subscriber) {
|
switch (this._mode) {
|
||||||
this._subscriber.subscribe(next);
|
case "byAuthor":
|
||||||
|
if (this._byAuthorFetcher) {
|
||||||
|
this._byAuthorFetcher.queue(next, 100);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "subscribe":
|
||||||
|
if (this._subscriber) {
|
||||||
|
this._subscriber.subscribe(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "data-latest":
|
case "data-latest":
|
||||||
|
@ -75,7 +91,7 @@ export class TimelineElement extends HTMLElement {
|
||||||
|
|
||||||
// if we made it this far, let's create a node
|
// if we made it this far, let's create a node
|
||||||
const e = document.createElement("li");
|
const e = document.createElement("li");
|
||||||
e.innerHTML = `<underbbs-message data-adapter="${this._adapter}" data-target="${next}" ${this._interactable ? "data-interactable" : ""} ${this._replyWith ? "data-replywith='" + this._replyWith + "'" : ""}></underbbs-message>`
|
e.innerHTML = `<underbbs-message data-adapter="${this._adapter}" data-target="${next}" ${this._interactable ? "data-interactable" : ""} ${this._replyWith ? "data-replywith='" + this._replyWith + "'" : ""} ${this._inspectWith ? "data-inspectwith='" + this._inspectWith + "'": ""}></underbbs-message>`
|
||||||
// second pass, try to place it in reverse-chronological order
|
// second pass, try to place it in reverse-chronological order
|
||||||
for (let i = 0; i < ul.childElementCount; i++){
|
for (let i = 0; i < ul.childElementCount; i++){
|
||||||
const id = ul.children[i]?.children[0]?.getAttribute("data-target");
|
const id = ul.children[i]?.children[0]?.getAttribute("data-target");
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { DatagramSocket } from './websocket'
|
import { DatagramSocket } from './websocket'
|
||||||
import { BatchTimer } from './batch-timer'
|
|
||||||
|
|
||||||
function _(key: string, value: any | null | undefined = undefined): any | null {
|
function _(key: string, value: any | null | undefined = undefined): any | null {
|
||||||
const x = <any>window;
|
const x = <any>window;
|
||||||
|
|
|
@ -3,12 +3,12 @@ import {AdapterState, AdapterData} from "./adapter";
|
||||||
import {Message, Attachment, Author} from "./message"
|
import {Message, Attachment, Author} from "./message"
|
||||||
import {Settings} from "./settings"
|
import {Settings} from "./settings"
|
||||||
import {SettingsElement} from "./settings-element"
|
import {SettingsElement} from "./settings-element"
|
||||||
import {AuthorMessagesElement} from "./author-messages-element"
|
|
||||||
import {ProfileElement} from "./profile-element"
|
import {ProfileElement} from "./profile-element"
|
||||||
import {MessageElement} from "./message-element"
|
import {MessageElement} from "./message-element"
|
||||||
import {TimelineElement} from "./timeline-element"
|
import {TimelineElement} from "./timeline-element"
|
||||||
import {TimelineFilterElement} from "./timeline-filter-element"
|
import {TimelineFilterElement} from "./timeline-filter-element"
|
||||||
import {CreateMessageElement} from "./create-message-element"
|
import {CreateMessageElement} from "./create-message-element"
|
||||||
|
import {NavigatorElement} from "./navigator"
|
||||||
|
|
||||||
export class DatagramSocket {
|
export class DatagramSocket {
|
||||||
public static skey: string | null = null;
|
public static skey: string | null = null;
|
||||||
|
@ -39,11 +39,6 @@ export class DatagramSocket {
|
||||||
const target = p.getAttribute("data-target");
|
const target = p.getAttribute("data-target");
|
||||||
p.setAttribute("data-target", target ?? "");
|
p.setAttribute("data-target", target ?? "");
|
||||||
});
|
});
|
||||||
const feeds = document.querySelectorAll("underbbs-author-messages");
|
|
||||||
feeds.forEach(f=>{
|
|
||||||
const target = f.getAttribute("data-target");
|
|
||||||
f.setAttribute("data-target", target ?? "");
|
|
||||||
});
|
|
||||||
const timelines = document.querySelectorAll("underbbs-timeline");
|
const timelines = document.querySelectorAll("underbbs-timeline");
|
||||||
timelines.forEach(t=>{
|
timelines.forEach(t=>{
|
||||||
const target = t.getAttribute("data-target");
|
const target = t.getAttribute("data-target");
|
||||||
|
@ -130,10 +125,10 @@ function init() {
|
||||||
customElements.define("underbbs-message", MessageElement);
|
customElements.define("underbbs-message", MessageElement);
|
||||||
customElements.define("underbbs-settings", SettingsElement);
|
customElements.define("underbbs-settings", SettingsElement);
|
||||||
customElements.define("underbbs-profile", ProfileElement);
|
customElements.define("underbbs-profile", ProfileElement);
|
||||||
customElements.define("underbbs-author-messages", AuthorMessagesElement);
|
|
||||||
customElements.define("underbbs-timeline", TimelineElement);
|
customElements.define("underbbs-timeline", TimelineElement);
|
||||||
customElements.define("underbbs-timeline-filter", TimelineFilterElement);
|
customElements.define("underbbs-timeline-filter", TimelineFilterElement);
|
||||||
customElements.define("underbbs-create-message", CreateMessageElement);
|
customElements.define("underbbs-create-message", CreateMessageElement);
|
||||||
|
customElements.define("underbbs-navigator", NavigatorElement);
|
||||||
|
|
||||||
console.log("underbbs initialized!")
|
console.log("underbbs initialized!")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue