diff --git a/adapter/anonAp.go b/adapter/anonAp.go
index 5acc239..4098efd 100644
--- a/adapter/anonAp.go
+++ b/adapter/anonAp.go
@@ -225,11 +225,27 @@ func (self *anonAPAdapter) Fetch(etype string, ids []string) error {
if string([]byte{id[0]}) == "@" {
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 {
return err
}
+ fmt.Printf("%d\n", res.StatusCode)
data := getBodyJson(res)
+ fmt.Println(string(data))
wf := webFinger{}
json.Unmarshal(data, &wf)
var profile string
diff --git a/frontend/dist/style.css b/frontend/dist/style.css
index 7ee7d6a..d33cf9c 100644
--- a/frontend/dist/style.css
+++ b/frontend/dist/style.css
@@ -1,11 +1,3 @@
-:root {
- --bg_color: #000000;
- --fg_color: #ccc;
- --main_color: #1f9b92;
- --sub_color: #002b36;
- --err_color: #DC143C;
-}
-
* {
box-sizing: border-box;
padding: 0;
@@ -17,17 +9,6 @@
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 {
color: var(--main_color);
}
@@ -44,27 +25,23 @@ input {
color: var(--err_color);
}
-nav ul li {
- display: inline;
- padding: 0.5em;
+underbbs-message, underbbs-profile {
+ max-width: 70ch;
+ display: block;
+}
+
+underbbs-message img {
+ max-width: 100%;
+}
+
+underbbs-profile img {
+ max-width: 200px;
}
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;
- display: grid;
- grid-template-columns: 1fr 1fr;
- grid-template-rows: auto;
+underbbs-message .message_metadata span {
+ display: block;
}
\ No newline at end of file
diff --git a/frontend/ts/author-messages-element.ts b/frontend/ts/author-messages-element.ts
deleted file mode 100644
index d9c0081..0000000
--- a/frontend/ts/author-messages-element.ts
+++ /dev/null
@@ -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 = `
`;
- }
-
- 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 = ``
- // 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);
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/frontend/ts/batch-timer.ts b/frontend/ts/fetcher.ts
similarity index 97%
rename from frontend/ts/batch-timer.ts
rename to frontend/ts/fetcher.ts
index f11158b..15fb576 100644
--- a/frontend/ts/batch-timer.ts
+++ b/frontend/ts/fetcher.ts
@@ -1,6 +1,6 @@
import util from './util'
-export class BatchTimer {
+export class Fetcher {
private _batch: string[];
private _timer: number;
private _reqFn: (id: string[])=>void;
diff --git a/frontend/ts/message-element.ts b/frontend/ts/message-element.ts
index cc5fe6d..dd679ed 100644
--- a/frontend/ts/message-element.ts
+++ b/frontend/ts/message-element.ts
@@ -1,6 +1,6 @@
import util from "./util"
import { Message } from "./message"
-import { BatchTimer } from "./batch-timer"
+import { Fetcher } from "./fetcher"
import { AdapterState } from "./adapter"
export class MessageElement extends HTMLElement {
@@ -12,8 +12,9 @@ export class MessageElement extends HTMLElement {
private _message: Message | null = null;
private _interactable: boolean = false;
private _replyWith: string | null = null;
+ private _inspectWith: string | null = null;
- private _messageTimer: BatchTimer | null = null;
+ private _msgFetcher: Fetcher | null = null;
constructor() {
super();
@@ -24,9 +25,10 @@ export class MessageElement extends HTMLElement {
this._id = this.getAttribute("data-target");
this._adapter = this.getAttribute("data-adapter");
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._replyWith = this.getAttribute("data-replywith");
+ this._inspectWith = this.getAttribute("data-inspectwith");
}
attributeChangedCallback(attr: string, prev: string, next: string) {
@@ -38,8 +40,8 @@ export class MessageElement extends HTMLElement {
this._id = next;
this._message = null;
this.innerHTML = ``;
- if (this._messageTimer) {
- this._messageTimer.queue(next, 100);
+ if (this._msgFetcher) {
+ this._msgFetcher.queue(next, 100);
}
break;
case "data-latest":
@@ -49,7 +51,6 @@ export class MessageElement extends HTMLElement {
return;
}
let msg = datastore.messages.get(next);
- console.log("MessageElement.attributeChangedCallback: " + JSON.stringify(msg));
if (msg) {
this._message = msg;
const metadata = this.querySelector(".message_metadata");
@@ -58,11 +59,29 @@ export class MessageElement extends HTMLElement {
const interactions = this.querySelector(".message_interactions");
if (metadata) {
if (this._message.renoteId) {
- metadata.innerHTML = `${this._message.renoter}${new Date(this._message.renoteTime ?? 0)}`
+ metadata.innerHTML = `${this._message.renoter} boosted${new Date(this._message.renoteTime ?? 0)}`
} else {
metadata.innerHTML = "";
}
- metadata.innerHTML += `${this._message.author}${new Date(this._message.created)}${this._message.visibility}${this._message.protocol}`
+ metadata.innerHTML += `${this._message.author}${new Date(this._message.created)}${this._message.uri}${this._message.replyTo ? "reply to " + this._message.replyTo + "" : ""}${this._message.visibility}${this._message.protocol}`
+
+ 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) {
content.innerHTML = this._message.content;
@@ -110,7 +129,7 @@ export class MessageElement extends HTMLElement {
attachments.innerHTML = html;
}
if (this._interactable && interactions) {
- interactions.innerHTML = `permalink`
+ interactions.innerHTML = ``
const replyBtn = this.querySelector(".message_reply");
const boostBtn = this.querySelector(".message_boost");
if (replyBtn) {
@@ -139,4 +158,17 @@ export class MessageElement extends HTMLElement {
private 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");
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/frontend/ts/navigator.ts b/frontend/ts/navigator.ts
new file mode 100644
index 0000000..c4132eb
--- /dev/null
+++ b/frontend/ts/navigator.ts
@@ -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 = ``
+ }
+
+ 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 = ``
+
+ 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 = ``
+ 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() {
+ }
+}
\ No newline at end of file
diff --git a/frontend/ts/profile-element.ts b/frontend/ts/profile-element.ts
index 4ffd2df..e5bbe9a 100644
--- a/frontend/ts/profile-element.ts
+++ b/frontend/ts/profile-element.ts
@@ -1,6 +1,6 @@
import { Author } from "./message"
import util from "./util"
-import { BatchTimer } from "./batch-timer"
+import { Fetcher } from "./fetcher"
import { AdapterState } from "./adapter"
export class ProfileElement extends HTMLElement {
@@ -11,7 +11,7 @@ export class ProfileElement extends HTMLElement {
private _author: Author | null = null;
- private _authorTimer: BatchTimer | null = null;
+ private _authorFetcher: Fetcher | null = null;
constructor() {
@@ -24,7 +24,7 @@ export class ProfileElement extends HTMLElement {
this._id = this.getAttribute("data-target");
this._adapter = this.getAttribute("data-adapter") ?? "";
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) {
@@ -35,8 +35,8 @@ export class ProfileElement extends HTMLElement {
return
}
this._id = next;
- if (this._authorTimer) {
- this._authorTimer.queue(next, 100);
+ if (this._authorFetcher) {
+ this._authorFetcher.queue(next, 100);
}
break;
case "data-latest":
diff --git a/frontend/ts/timeline-element.ts b/frontend/ts/timeline-element.ts
index 472b1fb..b635e1f 100644
--- a/frontend/ts/timeline-element.ts
+++ b/frontend/ts/timeline-element.ts
@@ -1,6 +1,7 @@
import { Author, Message } from "./message"
import util from "./util"
import { Subscriber } from "./subscriber"
+import { Fetcher } from "./fetcher"
import { AdapterState } from "./adapter"
export class TimelineElement extends HTMLElement {
@@ -11,10 +12,13 @@ export class TimelineElement extends HTMLElement {
private _interactable: boolean = false;
private _replyWith: string | null = null;
+ private _inspectWith: string | null = null;
+ private _mode: string = "subscribe";
private _messages: Message[] = [];
private _subscriber: Subscriber | null = null;
-
+ private _byAuthorFetcher: Fetcher | null = null;
+
constructor() {
super();
this.innerHTML = ``;
@@ -25,8 +29,11 @@ export class TimelineElement extends HTMLElement {
this._adapter = this.getAttribute("data-adapter") ?? "";
const gateway = this.getAttribute("data-gateway") ?? "";
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._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) {
@@ -38,8 +45,17 @@ export class TimelineElement extends HTMLElement {
this._timeline = next;
this.innerHTML = ``;
this._messages = [];
- if (this._subscriber) {
- this._subscriber.subscribe(next);
+ switch (this._mode) {
+ case "byAuthor":
+ if (this._byAuthorFetcher) {
+ this._byAuthorFetcher.queue(next, 100);
+ }
+ break;
+ case "subscribe":
+ if (this._subscriber) {
+ this._subscriber.subscribe(next);
+ }
+ break;
}
break;
case "data-latest":
@@ -75,7 +91,7 @@ export class TimelineElement extends HTMLElement {
// if we made it this far, let's create a node
const e = document.createElement("li");
- e.innerHTML = ``
+ e.innerHTML = ``
// 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");
diff --git a/frontend/ts/util.ts b/frontend/ts/util.ts
index a0f417e..365a9d5 100644
--- a/frontend/ts/util.ts
+++ b/frontend/ts/util.ts
@@ -1,5 +1,4 @@
import { DatagramSocket } from './websocket'
-import { BatchTimer } from './batch-timer'
function _(key: string, value: any | null | undefined = undefined): any | null {
const x = window;
diff --git a/frontend/ts/websocket.ts b/frontend/ts/websocket.ts
index bff8e2f..0ff1ced 100644
--- a/frontend/ts/websocket.ts
+++ b/frontend/ts/websocket.ts
@@ -3,12 +3,12 @@ import {AdapterState, AdapterData} from "./adapter";
import {Message, Attachment, Author} from "./message"
import {Settings} from "./settings"
import {SettingsElement} from "./settings-element"
-import {AuthorMessagesElement} from "./author-messages-element"
import {ProfileElement} from "./profile-element"
import {MessageElement} from "./message-element"
import {TimelineElement} from "./timeline-element"
import {TimelineFilterElement} from "./timeline-filter-element"
import {CreateMessageElement} from "./create-message-element"
+import {NavigatorElement} from "./navigator"
export class DatagramSocket {
public static skey: string | null = null;
@@ -39,11 +39,6 @@ export class DatagramSocket {
const target = p.getAttribute("data-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");
timelines.forEach(t=>{
const target = t.getAttribute("data-target");
@@ -130,10 +125,10 @@ function init() {
customElements.define("underbbs-message", MessageElement);
customElements.define("underbbs-settings", SettingsElement);
customElements.define("underbbs-profile", ProfileElement);
- customElements.define("underbbs-author-messages", AuthorMessagesElement);
customElements.define("underbbs-timeline", TimelineElement);
customElements.define("underbbs-timeline-filter", TimelineFilterElement);
customElements.define("underbbs-create-message", CreateMessageElement);
+ customElements.define("underbbs-navigator", NavigatorElement);
console.log("underbbs initialized!")
}