2023-10-07 20:40:18 +00:00
|
|
|
import { ConversationListRetriever, GroupChatListGetter, NewMessageChecker } from "./conversation-list.tsx";
|
|
|
|
import { PublicKey } from "../lib/nostr-ts/key.ts";
|
2023-08-28 17:58:05 +00:00
|
|
|
import { NostrAccountContext, NostrEvent, NostrKind } from "../lib/nostr-ts/nostr.ts";
|
2023-10-03 18:25:36 +00:00
|
|
|
import { getTags, Profile_Nostr_Event, Text_Note_Event } from "../nostr.ts";
|
2023-10-03 15:15:45 +00:00
|
|
|
import { ProfileSyncer } from "../features/profile.ts";
|
2023-10-04 15:07:40 +00:00
|
|
|
import { GroupChatCreation } from "../group-chat.ts";
|
2023-06-30 14:05:57 +00:00
|
|
|
|
2023-09-23 21:54:13 +00:00
|
|
|
export interface ConversationSummary {
|
2023-06-30 14:05:57 +00:00
|
|
|
pubkey: PublicKey;
|
2023-09-13 18:27:08 +00:00
|
|
|
profile: Profile_Nostr_Event | undefined;
|
2023-06-30 14:05:57 +00:00
|
|
|
newestEventSendByMe: NostrEvent | undefined;
|
|
|
|
newestEventReceivedByMe: NostrEvent | undefined;
|
|
|
|
}
|
|
|
|
|
2023-09-23 21:54:13 +00:00
|
|
|
export function getConversationSummaryFromPublicKey(k: PublicKey, users: Map<string, ConversationSummary>) {
|
|
|
|
return users.get(k.hex);
|
2023-08-24 13:41:50 +00:00
|
|
|
}
|
|
|
|
|
2023-10-07 20:40:18 +00:00
|
|
|
export class ConversationLists implements ConversationListRetriever, GroupChatListGetter, NewMessageChecker {
|
2023-09-24 21:56:29 +00:00
|
|
|
readonly convoSummaries = new Map<string, ConversationSummary>();
|
|
|
|
readonly groupChatSummaries = new Map<string, ConversationSummary>();
|
2023-10-03 15:15:45 +00:00
|
|
|
private readonly profile = new Map<string, Profile_Nostr_Event>();
|
2023-07-15 16:40:21 +00:00
|
|
|
|
2023-09-23 21:54:13 +00:00
|
|
|
constructor(
|
|
|
|
public readonly ctx: NostrAccountContext,
|
2023-10-03 15:15:45 +00:00
|
|
|
private readonly profileSyncer: ProfileSyncer,
|
2023-09-23 21:54:13 +00:00
|
|
|
) {}
|
2023-07-15 16:40:21 +00:00
|
|
|
|
2023-10-07 20:40:18 +00:00
|
|
|
has(hex: string, isGourpChat: boolean): boolean {
|
|
|
|
// todo: implement NewMessageChecker
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-09-13 14:52:01 +00:00
|
|
|
*getStrangers() {
|
2023-09-27 20:04:01 +00:00
|
|
|
for (const convoSummary of this.convoSummaries.values()) {
|
2023-09-13 14:52:01 +00:00
|
|
|
if (
|
2023-09-27 20:04:01 +00:00
|
|
|
(
|
|
|
|
convoSummary.newestEventReceivedByMe == undefined ||
|
|
|
|
convoSummary.newestEventSendByMe == undefined
|
|
|
|
) &&
|
|
|
|
!(
|
|
|
|
convoSummary.newestEventReceivedByMe == undefined &&
|
|
|
|
convoSummary.newestEventSendByMe == undefined
|
|
|
|
)
|
2023-09-13 14:52:01 +00:00
|
|
|
) {
|
2023-09-27 20:04:01 +00:00
|
|
|
yield convoSummary;
|
2023-09-13 14:52:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*getContacts() {
|
2023-09-24 21:56:29 +00:00
|
|
|
for (const userInfo of this.convoSummaries.values()) {
|
2023-09-13 14:52:01 +00:00
|
|
|
if (
|
|
|
|
userInfo.newestEventReceivedByMe != undefined &&
|
|
|
|
userInfo.newestEventSendByMe != undefined
|
|
|
|
) {
|
|
|
|
yield userInfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-24 21:56:29 +00:00
|
|
|
*getGroupChat() {
|
|
|
|
for (const value of this.groupChatSummaries.values()) {
|
|
|
|
yield value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-04 21:34:43 +00:00
|
|
|
getConversationType(pubkey: PublicKey, isGroupChat: boolean) {
|
|
|
|
if (isGroupChat) {
|
|
|
|
return "Group";
|
|
|
|
}
|
2023-10-03 18:25:36 +00:00
|
|
|
const contact = this.convoSummaries.get(pubkey.hex);
|
|
|
|
if (contact == undefined) {
|
|
|
|
return "Strangers";
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
contact.newestEventReceivedByMe == undefined || contact.newestEventSendByMe == undefined
|
|
|
|
) {
|
|
|
|
return "Strangers";
|
|
|
|
} else {
|
|
|
|
return "Contacts";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-04 15:07:40 +00:00
|
|
|
addGroupCreation(groupChatCreation: GroupChatCreation) {
|
2023-10-07 10:59:06 +00:00
|
|
|
const publicKey = groupChatCreation.groupKey.publicKey;
|
2023-10-04 15:07:40 +00:00
|
|
|
this.groupChatSummaries.set(publicKey.hex, {
|
|
|
|
pubkey: publicKey,
|
|
|
|
newestEventReceivedByMe: undefined,
|
|
|
|
newestEventSendByMe: undefined,
|
|
|
|
profile: this.profile.get(publicKey.hex),
|
|
|
|
});
|
|
|
|
this.profileSyncer.add(publicKey.hex);
|
|
|
|
}
|
|
|
|
|
|
|
|
addEvents(
|
2023-10-03 15:15:45 +00:00
|
|
|
events: (
|
|
|
|
| Profile_Nostr_Event
|
|
|
|
| Text_Note_Event
|
|
|
|
| NostrEvent<NostrKind.DIRECT_MESSAGE>
|
|
|
|
)[],
|
|
|
|
) {
|
2023-07-15 16:40:21 +00:00
|
|
|
// const t = Date.now();
|
|
|
|
for (const event of events) {
|
2023-06-30 14:05:57 +00:00
|
|
|
switch (event.kind) {
|
|
|
|
case NostrKind.META_DATA:
|
|
|
|
{
|
2023-10-04 15:07:40 +00:00
|
|
|
const profile = this.profile.get(event.publicKey.hex);
|
|
|
|
if (profile) {
|
|
|
|
if (profile.created_at < event.created_at) {
|
|
|
|
this.profile.set(event.publicKey.hex, event);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.profile.set(event.publicKey.hex, event);
|
|
|
|
}
|
2023-10-03 15:15:45 +00:00
|
|
|
const convoSummary = this.convoSummaries.get(event.pubkey);
|
|
|
|
const groupChatSummary = this.groupChatSummaries.get(event.pubkey);
|
|
|
|
|
|
|
|
if (convoSummary) {
|
|
|
|
if (convoSummary.profile) {
|
2023-10-04 15:07:40 +00:00
|
|
|
if (event.created_at > convoSummary.profile?.created_at) {
|
|
|
|
convoSummary.profile = event;
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
|
|
|
} else {
|
2023-10-04 15:07:40 +00:00
|
|
|
convoSummary.profile = event;
|
2023-10-03 15:15:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (groupChatSummary) {
|
|
|
|
if (groupChatSummary.profile) {
|
2023-10-04 15:07:40 +00:00
|
|
|
if (event.created_at > groupChatSummary.profile?.created_at) {
|
|
|
|
groupChatSummary.profile = event;
|
2023-10-03 15:15:45 +00:00
|
|
|
}
|
|
|
|
} else {
|
2023-10-04 15:07:40 +00:00
|
|
|
groupChatSummary.profile = event;
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NostrKind.DIRECT_MESSAGE:
|
|
|
|
{
|
|
|
|
let whoAm_I_TalkingTo = "";
|
2023-07-15 16:40:21 +00:00
|
|
|
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];
|
2023-07-15 16:40:21 +00:00
|
|
|
} 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;
|
|
|
|
}
|
2023-09-24 21:56:29 +00:00
|
|
|
const userInfo = this.convoSummaries.get(whoAm_I_TalkingTo);
|
2023-06-30 14:05:57 +00:00
|
|
|
if (userInfo) {
|
2023-09-23 20:53:40 +00:00
|
|
|
// userInfo.events.push(event);
|
2023-07-15 16:40:21 +00:00
|
|
|
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 {
|
2023-07-15 16:40:21 +00:00
|
|
|
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 {
|
2023-09-23 21:54:13 +00:00
|
|
|
const newUserInfo: ConversationSummary = {
|
2023-06-30 14:05:57 +00:00
|
|
|
pubkey: PublicKey.FromHex(whoAm_I_TalkingTo) as PublicKey,
|
|
|
|
newestEventReceivedByMe: undefined,
|
|
|
|
newestEventSendByMe: undefined,
|
2023-10-03 15:15:45 +00:00
|
|
|
profile: this.profile.get(whoAm_I_TalkingTo),
|
2023-06-30 14:05:57 +00:00
|
|
|
};
|
2023-07-15 16:40:21 +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 {
|
2023-07-15 16:40:21 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2023-09-24 21:56:29 +00:00
|
|
|
this.convoSummaries.set(whoAm_I_TalkingTo, newUserInfo);
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-23 21:54:13 +00:00
|
|
|
export const sortUserInfo = (a: ConversationSummary, b: ConversationSummary) => {
|
2023-06-30 14:05:57 +00:00
|
|
|
return sortScore(b) - sortScore(a);
|
|
|
|
};
|
|
|
|
|
2023-09-23 21:54:13 +00:00
|
|
|
function sortScore(contact: ConversationSummary) {
|
2023-06-30 14:05:57 +00:00
|
|
|
let score = 0;
|
|
|
|
if (contact.newestEventSendByMe !== undefined) {
|
|
|
|
score += contact.newestEventSendByMe.created_at;
|
|
|
|
}
|
|
|
|
if (contact.newestEventReceivedByMe !== undefined) {
|
|
|
|
score += contact.newestEventReceivedByMe.created_at;
|
|
|
|
}
|
|
|
|
return score;
|
|
|
|
}
|