lining things up for UI goodness
This commit is contained in:
parent
2244bbcf7a
commit
b8429533df
6 changed files with 75 additions and 149 deletions
|
@ -99,24 +99,24 @@ func (self *MastoAdapter) DefaultSubscriptionFilter() string {
|
|||
}
|
||||
|
||||
func (self *MastoAdapter) mastoUpdateToMessage(status madon.Status) *Message {
|
||||
var parent *madon.Status
|
||||
|
||||
if status.InReplyToID != nil {
|
||||
parent, _ = self.masto.GetStatus(*status.InReplyToID)
|
||||
}
|
||||
msg := Message{
|
||||
Protocol: "mastodon",
|
||||
Datagram: Datagram{
|
||||
Protocol: "mastodon",
|
||||
Adapter: self.nickname,
|
||||
Id: fmt.Sprintf("%d", status.ID),
|
||||
Uri: status.URI,
|
||||
Type: "message",
|
||||
},
|
||||
Content: status.Content,
|
||||
Uri: status.URI,
|
||||
Author: status.Account.Acct,
|
||||
Created: status.CreatedAt,
|
||||
Visibility: status.Visibility,
|
||||
}
|
||||
if parent != nil {
|
||||
msg.ReplyTo = &parent.URI
|
||||
if status.InReplyToID != nil {
|
||||
idStr := fmt.Sprintf("%d", *status.InReplyToID)
|
||||
msg.ReplyTo = &idStr
|
||||
}
|
||||
// TODO: mentions and replies
|
||||
msg.Aux = make(map[string]string)
|
||||
msg.Aux["visibility"] = status.Visibility
|
||||
|
||||
return &msg
|
||||
}
|
||||
|
|
|
@ -176,11 +176,17 @@ func (self *MisskeyAdapter) cacheAndConvert(n mkm.Note) *Message {
|
|||
if !exists || timestamp.Before(n.CreatedAt) {
|
||||
self.cache[n.ID] = n.CreatedAt
|
||||
msg := Message{
|
||||
Uri: n.URI,
|
||||
Datagram: Datagram{
|
||||
Id: n.ID,
|
||||
Uri: n.URI,
|
||||
Protocol: "misskey",
|
||||
Adapter: self.nickname,
|
||||
Type: "message",
|
||||
},
|
||||
|
||||
Created: n.CreatedAt,
|
||||
|
||||
Author: fmt.Sprintf("%s@%s", n.User.Username, mkcore.StringValue(n.User.Host)),
|
||||
Protocol: "misskey",
|
||||
Adapter: self.nickname,
|
||||
Created: n.CreatedAt,
|
||||
Content: n.Text,
|
||||
Attachments: []Attachment{},
|
||||
Visibility: n.Visibility,
|
||||
|
|
|
@ -89,15 +89,18 @@ func (self *NostrAdapter) DefaultSubscriptionFilter() string {
|
|||
|
||||
func (self *NostrAdapter) nostrEventToMsg(evt *nostr.Event) (Message, error) {
|
||||
m := Message{
|
||||
Protocol: "nostr",
|
||||
Adapter: self.nickname,
|
||||
Datagram: Datagram{
|
||||
Protocol: "nostr",
|
||||
Adapter: self.nickname,
|
||||
Type: "message",
|
||||
},
|
||||
}
|
||||
if evt == nil {
|
||||
return m, errors.New("no event")
|
||||
}
|
||||
switch evt.Kind {
|
||||
case nostr.KindTextNote:
|
||||
m.Uri = evt.ID
|
||||
m.Id = evt.ID
|
||||
m.Author = evt.PubKey
|
||||
m.Created = evt.CreatedAt.Time()
|
||||
m.Content = evt.Content
|
||||
|
|
|
@ -5,11 +5,18 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
type Datagram struct {
|
||||
Id string
|
||||
Uri string
|
||||
Protocol string
|
||||
Adapter string
|
||||
Type string
|
||||
Target *string
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Uri string
|
||||
Datagram
|
||||
Author string
|
||||
Protocol string
|
||||
Adapter string
|
||||
Content string
|
||||
Attachments []Attachment
|
||||
ReplyTo *string
|
||||
|
@ -22,11 +29,11 @@ type Message struct {
|
|||
}
|
||||
|
||||
type Author struct {
|
||||
Id string
|
||||
Datagram
|
||||
Name string
|
||||
ProfileData interface{}
|
||||
ProfileUri string
|
||||
ProfilePic string
|
||||
Messages []Message
|
||||
}
|
||||
|
||||
type Attachment struct {
|
||||
|
|
127
ts/adapter.ts
127
ts/adapter.ts
|
@ -1,118 +1,17 @@
|
|||
import NDK, {NDKPrivateKeySigner} from "@nostr-dev-kit/ndk";
|
||||
import * as nip19 from 'nostr-tools/nip19'
|
||||
import { createRestAPIClient, createStreamingAPIClient } from "masto";
|
||||
import * as masto from "masto";
|
||||
|
||||
type MastodonClient = masto.mastodon.rest.Client;
|
||||
type MastodonStreamClient = masto.mastodon.streaming.Client;
|
||||
|
||||
export class MastodonCompoundClient {
|
||||
public rest: MastodonClient;
|
||||
public stream: MastodonStreamClient;
|
||||
import {Message, Author} from "./message"
|
||||
export class AdapterData {
|
||||
public protocol: string;
|
||||
public directMessages Map<string, Message>();
|
||||
public messages: Map<string, Message>();
|
||||
public profileCache: Map<string, Author>;
|
||||
|
||||
public constructor(c: MastodonClient, s: MastodonStreamClient) {
|
||||
this.rest = c;
|
||||
this.stream = s;
|
||||
constructor(protocol: string) {
|
||||
this.protocol = protocol;
|
||||
this.messages = [];
|
||||
this.profileCache = [];
|
||||
}
|
||||
}
|
||||
|
||||
export class Adapter {
|
||||
public nickname: string = "";
|
||||
public protocol: string = "";
|
||||
public identity: any | null;
|
||||
|
||||
private _self: NDK | MastodonCompoundClient | null = null ;
|
||||
|
||||
public init(): void {};
|
||||
public getInbox(): void {};
|
||||
public publish(): void {};
|
||||
public getFollowers(): any[] { return [] };
|
||||
public getFollowing(): any[] { return [] };
|
||||
public updateMetadata(): void {};
|
||||
public getMetadata(): any { return {} };
|
||||
|
||||
// according to the docs NDK must be a singleton...
|
||||
// this probalby will make having more than one nostr adapter at once problematic
|
||||
private static ndk: NDK | null = null;
|
||||
|
||||
public static create(): Adapter {
|
||||
return new Adapter();
|
||||
}
|
||||
|
||||
public static toNostr(adapter: Adapter, settings: any): Adapter {
|
||||
adapter.identity = { privkey: settings.privkey };
|
||||
adapter.nickname = settings.nickname;
|
||||
|
||||
adapter.init = ()=> {
|
||||
if (!Adapter.ndk) {
|
||||
let privkey_raw = nip19.decode(settings.privkey);
|
||||
Adapter.ndk = new NDK({
|
||||
signer: new NDKPrivateKeySigner(<string>privkey_raw.data),
|
||||
explicitRelayUrls: [ settings.relays ]
|
||||
});
|
||||
adapter._self = Adapter.ndk;
|
||||
Adapter.ndk.connect();
|
||||
} else {
|
||||
Adapter.ndk.signer = new NDKPrivateKeySigner(settings.privatekey);
|
||||
for (let i of settings.relays) {
|
||||
Adapter.ndk.addExplicitRelay(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
adapter.getInbox = async () => {
|
||||
if (Adapter.ndk) {
|
||||
const sub = Adapter.ndk.subscribe({ kinds: [1] }); // Get all kind:1s
|
||||
sub.on("event", (event) => console.log(event.content)); // Show the content
|
||||
sub.on("eose", () => console.log("All relays have reached the end of the event stream"));
|
||||
sub.on("close", () => console.log("Subscription closed"));
|
||||
setTimeout(() => sub.stop(), 10000); // Stop the subscription after 10 seconds
|
||||
}
|
||||
};
|
||||
|
||||
return adapter;
|
||||
}
|
||||
|
||||
public static toMasto(adapter: Adapter, settings: any): Adapter {
|
||||
adapter.identity = { server: settings.server, apiKey: settings.apiKey };
|
||||
adapter.nickname = settings.nickname;
|
||||
|
||||
adapter.init = () => {
|
||||
const rawServer: string = adapter.identity.server.split("://")[1];
|
||||
|
||||
adapter._self = new MastodonCompoundClient(createRestAPIClient({
|
||||
url: adapter.identity.server,
|
||||
accessToken: adapter.identity.apiKey
|
||||
}),
|
||||
createStreamingAPIClient({
|
||||
streamingApiUrl: `https://${rawServer}/v1/api/streaming`,
|
||||
accessToken: adapter.identity.apiKey
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
adapter.getInbox = async () => {
|
||||
const rawServer: string = adapter.identity.server.split("://")[1];
|
||||
let conn = new WebSocket(`wss://${rawServer}/streaming/?i=${adapter.identity.apiKey}`)
|
||||
conn.addEventListener("open", async (e:any)=> {
|
||||
console.log(e);
|
||||
let filter = { type: "connect", body: { channel: "localTimeline", id: crypto.randomUUID() }};
|
||||
let data = await JSON.stringify(filter);
|
||||
console.log(data);
|
||||
conn.send(data);
|
||||
conn.addEventListener("message", (e:any)=>{console.log(e)});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default { Adapter }
|
||||
|
||||
export interface AdapterState {
|
||||
[nickname: string]: AdapterData;
|
||||
}
|
35
ts/index.ts
35
ts/index.ts
|
@ -11,17 +11,10 @@ function main():void {
|
|||
|
||||
if (settings != null) {
|
||||
for (let s of settings.adapters ?? []) {
|
||||
let a: Adapter = Adapter.create()
|
||||
switch (s.protocol) {
|
||||
case "nostr":
|
||||
adapters.push(Adapter.toNostr(a, s));
|
||||
break;
|
||||
case "mastodon":
|
||||
adapters.push(Adapter.toMasto(a, s));
|
||||
}
|
||||
|
||||
}
|
||||
if (adapters.length > 0) {
|
||||
_("currentAdapter", 0);
|
||||
_("currentAdapter", adapters[0].nickname);
|
||||
// update tabbar and tabcontent with first adapter
|
||||
}
|
||||
} else {
|
||||
|
@ -197,6 +190,9 @@ async function authorizedFetch(method: string, uri: string, body: any): Promise<
|
|||
|
||||
function connect() {
|
||||
|
||||
var datastore: AdapterState = {}
|
||||
datastore = _("datastore", datastore);
|
||||
|
||||
const wsProto = location.protocol == "https:" ? "wss" : "ws";
|
||||
_conn = new WebSocket(`${wsProto}://${location.host}/subscribe`, "underbbs");
|
||||
_conn.addEventListener("open", (e: any) => {
|
||||
|
@ -214,13 +210,28 @@ function connect() {
|
|||
_("skey", data.key)
|
||||
authorizedFetch("POST", "/api/adapters", JSON.stringify(_("settings").adapters))
|
||||
} else {
|
||||
// typeswitch on the incoming data and adapters
|
||||
|
||||
// if it's a regular message, add it to the store
|
||||
if (!datastore[data.adapter]) {
|
||||
datastore[data.adapter] = new AdapterData(data.protocol);
|
||||
}
|
||||
|
||||
// typeswitch on the incoming data type and fill the memory
|
||||
switch (data.type) {
|
||||
case "message":
|
||||
datastore[data.adapter].messages[data.id] = <Message>data;
|
||||
break;
|
||||
case "author":
|
||||
datastore[data.adapter].profileCache[data.id] = <Author>data;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// if the adapter is active, inject the web components
|
||||
// FOR HOTFETCHED DATA:
|
||||
// before fetching, we can set properties on the DOM,
|
||||
// so when those data return to us we know where to inject components!
|
||||
if (_("currentAdapter") == data.adapter) {
|
||||
// dive in and insert that shit in the dom
|
||||
}
|
||||
}
|
||||
});
|
||||
_conn.addEventListener("error", (e: any) => {
|
||||
|
|
Loading…
Reference in a new issue