blowater/UI/contact-list.ts

257 lines
11 KiB
TypeScript
Raw Normal View History

2023-07-11 09:49:58 +00:00
import { Database_Contextual_View } from "../database.ts";
import { profilesStream } from "../features/profile.ts";
2023-06-30 14:05:57 +00:00
import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
import { ContactGroup } from "./contact-list.tsx";
import { PublicKey } from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/key.ts";
import {
NostrAccountContext,
NostrEvent,
NostrKind,
} from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nostr.ts";
import { ConnectionPool } from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/relay.ts";
import {
CustomAppData,
CustomAppData_Event,
getTags,
PlainText_Nostr_Event,
Profile_Nostr_Event,
} from "../nostr.ts";
2023-06-30 14:05:57 +00:00
export interface UserInfo {
pubkey: PublicKey;
2023-07-14 10:59:25 +00:00
profile: Profile_Nostr_Event | undefined; // todo: maybe change it to ProfileEvent
2023-06-30 14:05:57 +00:00
newestEventSendByMe: NostrEvent | undefined;
newestEventReceivedByMe: NostrEvent | undefined;
pinEvent: {
readonly created_at: number;
readonly content: CustomAppData;
} | undefined;
2023-07-15 17:42:39 +00:00
events: PlainText_Nostr_Event[];
2023-06-30 14:05:57 +00:00
}
export class ProfilesSyncer {
readonly userSet = new Set<string>();
private chan = new Channel<string[]>();
constructor(
2023-07-11 09:49:58 +00:00
private readonly database: Database_Contextual_View,
2023-06-30 14:05:57 +00:00
private readonly pool: ConnectionPool,
) {
(async () => {
let stream = profilesStream(this.userSet, pool);
database.syncEvents((e) => e.kind == NostrKind.META_DATA, stream);
for await (const users of this.chan) {
const size = this.userSet.size;
for (const user of users) {
this.userSet.add(user);
}
if (this.userSet.size > size) {
console.log("adding", users);
await stream.close();
stream = profilesStream(this.userSet, pool);
database.syncEvents((e) => e.kind == NostrKind.META_DATA, stream);
}
}
})();
}
async add(...users: string[]) {
const err = await this.chan.put(users);
if (err instanceof Error) {
throw err; // impossible
}
}
}
export function getUserInfoFromPublicKey(k: PublicKey, users: Map<string, UserInfo>) {
const userInfo = users.get(k.hex);
return userInfo;
}
export class AllUsersInformation {
readonly userInfos = new Map<string, UserInfo>();
constructor(public readonly ctx: NostrAccountContext) {}
addEvents(events: (Profile_Nostr_Event | PlainText_Nostr_Event | CustomAppData_Event)[]) {
// const t = Date.now();
for (const event of events) {
2023-06-30 14:05:57 +00:00
switch (event.kind) {
case NostrKind.META_DATA:
{
const userInfo = this.userInfos.get(event.pubkey);
2023-07-14 14:13:15 +00:00
const profileEvent = event;
2023-06-30 14:05:57 +00:00
if (userInfo) {
if (userInfo.profile) {
if (profileEvent.created_at > userInfo.profile?.created_at) {
userInfo.profile = profileEvent;
}
} else {
userInfo.profile = profileEvent;
}
} else {
const newUserInfo: UserInfo = {
pinEvent: undefined,
pubkey: PublicKey.FromHex(event.pubkey) as PublicKey,
newestEventReceivedByMe: undefined,
newestEventSendByMe: undefined,
2023-07-14 14:13:15 +00:00
profile: profileEvent,
2023-07-15 17:42:39 +00:00
events: [],
2023-06-30 14:05:57 +00:00
};
this.userInfos.set(event.pubkey, newUserInfo);
2023-06-30 14:05:57 +00:00
}
}
break;
case NostrKind.TEXT_NOTE:
break;
case NostrKind.RECOMMED_SERVER:
break;
case NostrKind.CONTACTS:
break;
case NostrKind.DIRECT_MESSAGE:
{
let whoAm_I_TalkingTo = "";
if (event.pubkey == this.ctx.publicKey.hex) {
2023-06-30 14:05:57 +00:00
// I am the sender
whoAm_I_TalkingTo = getTags(event).p[0];
} else if (getTags(event).p[0] == this.ctx.publicKey.hex) {
2023-06-30 14:05:57 +00:00
// I am the receiver
whoAm_I_TalkingTo = event.pubkey;
} else {
// I am neither. Possible because other user has used this device before
break;
}
const userInfo = this.userInfos.get(whoAm_I_TalkingTo);
2023-06-30 14:05:57 +00:00
if (userInfo) {
2023-07-15 17:42:39 +00:00
userInfo.events.push(event);
if (whoAm_I_TalkingTo == this.ctx.publicKey.hex) {
2023-06-30 14:05:57 +00:00
// talking to myself
if (userInfo.newestEventSendByMe) {
if (event.created_at > userInfo.newestEventSendByMe?.created_at) {
userInfo.newestEventSendByMe = event;
userInfo.newestEventReceivedByMe = event;
}
} else {
userInfo.newestEventSendByMe = event;
userInfo.newestEventReceivedByMe = event;
}
} else {
if (this.ctx.publicKey.hex == event.pubkey) {
2023-06-30 14:05:57 +00:00
// I am the sender
if (userInfo.newestEventSendByMe) {
if (event.created_at > userInfo.newestEventSendByMe.created_at) {
userInfo.newestEventSendByMe = event;
}
} else {
userInfo.newestEventSendByMe = event;
}
} else {
// I am the receiver
if (userInfo.newestEventReceivedByMe) {
if (event.created_at > userInfo.newestEventReceivedByMe.created_at) {
userInfo.newestEventReceivedByMe = event;
}
} else {
userInfo.newestEventReceivedByMe = event;
}
}
}
} else {
const newUserInfo: UserInfo = {
pubkey: PublicKey.FromHex(whoAm_I_TalkingTo) as PublicKey,
pinEvent: undefined,
newestEventReceivedByMe: undefined,
newestEventSendByMe: undefined,
profile: undefined,
2023-07-15 17:42:39 +00:00
events: [event],
2023-06-30 14:05:57 +00:00
};
if (whoAm_I_TalkingTo == this.ctx.publicKey.hex) {
2023-06-30 14:05:57 +00:00
// talking to myself
newUserInfo.newestEventSendByMe = event;
newUserInfo.newestEventReceivedByMe = event;
} else {
if (this.ctx.publicKey.hex == event.pubkey) {
2023-06-30 14:05:57 +00:00
// I am the sender
newUserInfo.newestEventSendByMe = event;
} else {
// I am the receiver
newUserInfo.newestEventReceivedByMe = event;
}
}
this.userInfos.set(whoAm_I_TalkingTo, newUserInfo);
2023-06-30 14:05:57 +00:00
}
}
break;
case NostrKind.DELETE:
break;
case NostrKind.CustomAppData: {
const obj = event.customAppData;
2023-06-30 14:05:57 +00:00
if (obj.type == "PinContact" || obj.type == "UnpinContact") {
const userInfo = this.userInfos.get(obj.pubkey);
2023-06-30 14:05:57 +00:00
if (userInfo) {
if (userInfo.pinEvent) {
if (event.created_at > userInfo.pinEvent.created_at) {
userInfo.pinEvent = {
content: obj,
created_at: event.created_at,
};
}
} else {
userInfo.pinEvent = {
content: obj,
created_at: event.created_at,
};
}
} else {
this.userInfos.set(obj.pubkey, {
2023-06-30 14:05:57 +00:00
pubkey: PublicKey.FromHex(obj.pubkey) as PublicKey, // todo: could throw
pinEvent: {
content: obj,
created_at: event.created_at,
},
newestEventReceivedByMe: undefined,
newestEventSendByMe: undefined,
profile: undefined,
2023-07-15 17:42:39 +00:00
events: [],
2023-06-30 14:05:57 +00:00
});
}
}
}
}
}
// console.log("AllUsersInformation:addEvents", Date.now() - t);
2023-06-30 14:05:57 +00:00
}
}
export const sortUserInfo = (a: UserInfo, b: UserInfo) => {
return sortScore(b) - sortScore(a);
};
function sortScore(contact: UserInfo) {
let score = 0;
if (contact.newestEventSendByMe !== undefined) {
score += contact.newestEventSendByMe.created_at;
}
if (contact.newestEventReceivedByMe !== undefined) {
score += contact.newestEventReceivedByMe.created_at;
}
return score;
}
export function getGroupOf(pubkey: PublicKey, allUserInfo: Map<string, UserInfo>): ContactGroup {
const contact = allUserInfo.get(pubkey.hex);
if (contact == undefined) {
return "Strangers";
}
console.log(contact);
if (
contact.newestEventReceivedByMe == undefined || contact.newestEventSendByMe == undefined
) {
return "Strangers";
} else {
return "Contacts";
}
}