mirror of
https://github.com/BlowaterNostr/blowater.git
synced 2024-10-18 15:43:20 +00:00
move DirectMessageGetter out from Database_Contextual_View (#229)
to DirectedMessageController
This commit is contained in:
parent
d6bbaed8e6
commit
f97a8c9dbf
48
UI/app.tsx
48
UI/app.tsx
@ -26,10 +26,11 @@ import { About } from "./about.tsx";
|
||||
import { ProfileSyncer } from "../features/profile.ts";
|
||||
import { Popover, PopOverInputChannel } from "./components/popover.tsx";
|
||||
import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
||||
import { GroupChatController, GroupChatSyncer } from "../group-chat.ts";
|
||||
import { GroupChatSyncer, GroupMessageController } from "../features/gm.ts";
|
||||
import { OtherConfig } from "./config-other.ts";
|
||||
import { ProfileGetter } from "./search.tsx";
|
||||
import { fromEvents } from "../time.ts";
|
||||
import { DirectedMessageController } from "../features/dm.ts";
|
||||
|
||||
export async function Start(database: DexieDatabase) {
|
||||
console.log("Start the application");
|
||||
@ -110,8 +111,9 @@ export class App {
|
||||
public readonly eventSyncer: EventSyncer,
|
||||
public readonly conversationLists: ConversationLists,
|
||||
public readonly relayConfig: RelayConfig,
|
||||
public readonly groupChatController: GroupChatController,
|
||||
public readonly groupChatController: GroupMessageController,
|
||||
public readonly lamport: time.LamportTime,
|
||||
public readonly dmController: DirectedMessageController,
|
||||
) {}
|
||||
|
||||
static async Start(args: {
|
||||
@ -137,24 +139,42 @@ export class App {
|
||||
const conversationLists = new ConversationLists(args.ctx, profileSyncer);
|
||||
conversationLists.addEvents(args.database.events);
|
||||
|
||||
const dmControl = new DirectedMessageController(args.ctx);
|
||||
|
||||
const groupSyncer = new GroupChatSyncer(args.database, args.pool);
|
||||
const groupChatController = new GroupChatController(
|
||||
const groupChatController = new GroupMessageController(
|
||||
args.ctx,
|
||||
conversationLists,
|
||||
groupSyncer,
|
||||
profileSyncer,
|
||||
);
|
||||
for (const e of args.database.events) {
|
||||
if (e.kind == NostrKind.Group_Message) {
|
||||
const err = await groupChatController.addEvent({
|
||||
...e,
|
||||
kind: e.kind,
|
||||
});
|
||||
if (err instanceof Error) {
|
||||
console.error(err.message);
|
||||
|
||||
(async () => {
|
||||
for (const e of args.database.events) {
|
||||
if (e.kind == NostrKind.Group_Message) {
|
||||
const err = await groupChatController.addEvent({
|
||||
...e,
|
||||
kind: e.kind,
|
||||
});
|
||||
if (err instanceof Error) {
|
||||
console.error(err.message);
|
||||
}
|
||||
} else if (e.kind == NostrKind.DIRECT_MESSAGE) {
|
||||
const error = await dmControl.addEvent({
|
||||
...e,
|
||||
kind: e.kind,
|
||||
});
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message); // should delete the event
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
// notify update loop to render
|
||||
// todo: directly call render instead of go through database update loop
|
||||
args.database.sourceOfChange.put(null);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
return new App(
|
||||
args.database,
|
||||
@ -170,6 +190,7 @@ export class App {
|
||||
relayConfig,
|
||||
groupChatController,
|
||||
lamport,
|
||||
dmControl,
|
||||
);
|
||||
}
|
||||
|
||||
@ -313,6 +334,7 @@ export class App {
|
||||
this.lamport,
|
||||
this.conversationLists,
|
||||
this.groupChatController,
|
||||
this.dmController,
|
||||
)
|
||||
) {
|
||||
const t = Date.now();
|
||||
@ -403,7 +425,7 @@ export function AppComponent(props: {
|
||||
rightPanelModel: model.rightPanelModel,
|
||||
bus: app.eventBus,
|
||||
ctx: myAccountCtx,
|
||||
dmGetter: app.database,
|
||||
dmGetter: app.dmController,
|
||||
profileGetter: app.database,
|
||||
pool: props.pool,
|
||||
conversationLists: app.conversationLists,
|
||||
|
@ -9,7 +9,7 @@ import * as csp from "https://raw.githubusercontent.com/BlowaterNostr/csp/master
|
||||
import { Database_Contextual_View } from "../database.ts";
|
||||
import { convertEventsToChatMessages } from "./dm.ts";
|
||||
|
||||
import { sendDMandImages } from "../features/dm.ts";
|
||||
import { DirectedMessageController, sendDMandImages } from "../features/dm.ts";
|
||||
import { notify } from "./notification.ts";
|
||||
import { EventBus } from "../event-bus.ts";
|
||||
import { ContactUpdate } from "./conversation-list.tsx";
|
||||
@ -25,6 +25,7 @@ import {
|
||||
DirectedMessage_Event,
|
||||
Encrypted_Event,
|
||||
getTags,
|
||||
Parsed_Event,
|
||||
PinConversation,
|
||||
Profile_Nostr_Event,
|
||||
UnpinConversation,
|
||||
@ -43,7 +44,7 @@ import { NostrAccountContext, NostrEvent, NostrKind } from "../lib/nostr-ts/nost
|
||||
import { ConnectionPool } from "../lib/nostr-ts/relay.ts";
|
||||
import { OtherConfig } from "./config-other.ts";
|
||||
import { EditGroup, EditGroupChatProfile, StartEditGroupChatProfile } from "./edit-group.tsx";
|
||||
import { GroupChatController } from "../group-chat.ts";
|
||||
import { GroupMessageController } from "../features/gm.ts";
|
||||
import { ChatMessage } from "./message.ts";
|
||||
|
||||
export type UI_Interaction_Event =
|
||||
@ -481,14 +482,6 @@ export function getConversationMessages(args: {
|
||||
}
|
||||
|
||||
const messages = convertEventsToChatMessages(events);
|
||||
if (messages.length > 0) {
|
||||
messages.sort((m1, m2) => {
|
||||
if (m1.lamport && m2.lamport && m1.lamport != m2.lamport) {
|
||||
return m1.lamport - m2.lamport;
|
||||
}
|
||||
return m1.created_at.getTime() - m2.created_at.getTime();
|
||||
});
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
@ -521,14 +514,15 @@ export async function* Database_Update(
|
||||
profileSyncer: ProfileSyncer,
|
||||
lamport: LamportTime,
|
||||
convoLists: ConversationLists,
|
||||
groupController: GroupChatController,
|
||||
groupController: GroupMessageController,
|
||||
dmController: DirectedMessageController,
|
||||
) {
|
||||
const changes = database.subscribe();
|
||||
while (true) {
|
||||
await csp.sleep(333);
|
||||
await changes.ready();
|
||||
const t = Date.now();
|
||||
const changes_events: (Encrypted_Event | Profile_Nostr_Event | NostrEvent)[] = [];
|
||||
const changes_events: (Encrypted_Event | Profile_Nostr_Event | Parsed_Event)[] = [];
|
||||
while (true) {
|
||||
if (!changes.isReadyToPop()) {
|
||||
break;
|
||||
@ -587,10 +581,12 @@ export async function* Database_Update(
|
||||
model.myProfile = newProfile.profile;
|
||||
}
|
||||
} else if (e.kind == NostrKind.DIRECT_MESSAGE) {
|
||||
const pubkey = PublicKey.FromHex(e.pubkey);
|
||||
if (pubkey instanceof Error) {
|
||||
console.error(pubkey);
|
||||
continue;
|
||||
const err = await dmController.addEvent({
|
||||
...e,
|
||||
kind: e.kind,
|
||||
});
|
||||
if (err instanceof Error) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
} else if (e.kind == NostrKind.Group_Message) {
|
||||
@ -628,10 +624,6 @@ export async function* Database_Update(
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// if (hasKind_1) {
|
||||
// console.log("Database_Update: getSocialPosts");
|
||||
// model.social.threads = getSocialPosts(database, convoLists.convoSummaries);
|
||||
// }
|
||||
yield model;
|
||||
}
|
||||
}
|
||||
@ -656,7 +648,7 @@ export async function handle_SendMessage(
|
||||
dmEditors: Map<string, EditorModel>,
|
||||
gmEditors: Map<string, EditorModel>,
|
||||
db: Database_Contextual_View,
|
||||
groupControl: GroupChatController,
|
||||
groupControl: GroupMessageController,
|
||||
) {
|
||||
if (event.isGroupChat) {
|
||||
const groupCtx = groupControl.getGroupChatCtx(event.pubkey);
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ConversationListRetriever, NewMessageChecker } from "./conversation-list.tsx";
|
||||
import { PublicKey } from "../lib/nostr-ts/key.ts";
|
||||
import { NostrAccountContext, NostrEvent, NostrKind } from "../lib/nostr-ts/nostr.ts";
|
||||
import { getTags, Parsed_Event, Profile_Nostr_Event } from "../nostr.ts";
|
||||
import { getTags, Parsed_Event } from "../nostr.ts";
|
||||
import { ProfileSyncer } from "../features/profile.ts";
|
||||
import { GroupChatCreation } from "../group-chat.ts";
|
||||
import { GroupChatCreation } from "../features/gm.ts";
|
||||
|
||||
export interface ConversationSummary {
|
||||
pubkey: PublicKey;
|
||||
|
@ -27,8 +27,8 @@ export interface ConversationListRetriever {
|
||||
getConversationType(pubkey: PublicKey, isGourpChat: boolean): "Contacts" | "Strangers" | "Group";
|
||||
}
|
||||
|
||||
export interface GroupChatListGetter {
|
||||
getGroupChat: () => Iterable<ConversationSummary>;
|
||||
export interface GroupMessageListGetter {
|
||||
getConversationList: () => Iterable<ConversationSummary>;
|
||||
}
|
||||
|
||||
export type ConversationType = "Contacts" | "Strangers" | "Group";
|
||||
@ -47,7 +47,7 @@ type Props = {
|
||||
emit: emitFunc<ContactUpdate | SearchUpdate>;
|
||||
eventBus: EventSubscriber<UI_Interaction_Event>;
|
||||
convoListRetriever: ConversationListRetriever;
|
||||
groupChatListGetter: GroupChatListGetter;
|
||||
groupChatListGetter: GroupMessageListGetter;
|
||||
hasNewMessages: NewMessageChecker;
|
||||
pinListGetter: PinListGetter;
|
||||
profileGetter: ProfileGetter;
|
||||
@ -86,7 +86,7 @@ export class ConversationList extends Component<Props, State> {
|
||||
let listToRender: ConversationSummary[];
|
||||
const contacts = Array.from(props.convoListRetriever.getContacts());
|
||||
const strangers = Array.from(props.convoListRetriever.getStrangers());
|
||||
const groups = Array.from(props.groupChatListGetter.getGroupChat());
|
||||
const groups = Array.from(props.groupChatListGetter.getConversationList());
|
||||
switch (this.state.selectedContactGroup) {
|
||||
case "Contacts":
|
||||
listToRender = contacts;
|
||||
|
@ -21,7 +21,7 @@ import { EventSyncer } from "./event_syncer.ts";
|
||||
import { ButtonGroup } from "./components/button-group.tsx";
|
||||
import { PrimaryTextColor } from "./style/colors.ts";
|
||||
import { SettingIcon } from "./icons2/setting-icon.tsx";
|
||||
import { GroupChatController } from "../group-chat.ts";
|
||||
import { GroupMessageController } from "../features/gm.ts";
|
||||
import { ProfileGetter } from "./search.tsx";
|
||||
import { InviteIcon } from "./icons2/invite-icon.tsx";
|
||||
import { PublicKey } from "../lib/nostr-ts/key.ts";
|
||||
@ -33,7 +33,7 @@ type DirectMessageContainerProps = {
|
||||
bus: EventBus<UI_Interaction_Event>;
|
||||
profilesSyncer: ProfileSyncer;
|
||||
eventSyncer: EventSyncer;
|
||||
groupChatController: GroupChatController;
|
||||
groupChatController: GroupMessageController;
|
||||
// getters
|
||||
profileGetter: ProfileGetter;
|
||||
dmGetter: DirectMessageGetter;
|
||||
|
159
database.ts
159
database.ts
@ -1,11 +1,4 @@
|
||||
import {
|
||||
compare,
|
||||
DirectedMessage_Event,
|
||||
Encrypted_Event,
|
||||
getTags,
|
||||
Parsed_Event,
|
||||
Profile_Nostr_Event,
|
||||
} from "./nostr.ts";
|
||||
import { Encrypted_Event, getTags, Parsed_Event, Profile_Nostr_Event } from "./nostr.ts";
|
||||
import * as csp from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
||||
import { parseJSON, ProfileData } from "./features/profile.ts";
|
||||
import { parseContent } from "./UI/message.ts";
|
||||
@ -20,7 +13,6 @@ import {
|
||||
} from "./lib/nostr-ts/nostr.ts";
|
||||
import { PublicKey } from "./lib/nostr-ts/key.ts";
|
||||
import { NoteID } from "./lib/nostr-ts/nip19.ts";
|
||||
import { DirectMessageGetter } from "./UI/app_update.tsx";
|
||||
import { ProfileController } from "./UI/search.tsx";
|
||||
|
||||
const buffer_size = 2000;
|
||||
@ -50,11 +42,9 @@ export interface EventPutter {
|
||||
|
||||
export type EventsAdapter = EventsFilter & EventRemover & EventGetter & EventPutter;
|
||||
|
||||
type Accepted_Event = Encrypted_Event | Profile_Nostr_Event | NostrEvent;
|
||||
export class Database_Contextual_View implements DirectMessageGetter, ProfileController, EventGetter {
|
||||
private readonly sourceOfChange = csp.chan<Accepted_Event | null>(buffer_size);
|
||||
private readonly caster = csp.multi<Accepted_Event | null>(this.sourceOfChange);
|
||||
public readonly directed_messages = new Map<string, DirectedMessage_Event>();
|
||||
export class Database_Contextual_View implements ProfileController, EventGetter {
|
||||
public readonly sourceOfChange = csp.chan<Parsed_Event | null>(buffer_size);
|
||||
private readonly caster = csp.multi<Parsed_Event | null>(this.sourceOfChange);
|
||||
public readonly profiles = new Map<string, Profile_Nostr_Event>();
|
||||
|
||||
private constructor(
|
||||
@ -63,6 +53,50 @@ export class Database_Contextual_View implements DirectMessageGetter, ProfileCon
|
||||
private readonly ctx: NostrAccountContext,
|
||||
) {}
|
||||
|
||||
static async New(eventsAdapter: EventsAdapter, ctx: NostrAccountContext) {
|
||||
const t = Date.now();
|
||||
const allEvents = await eventsAdapter.filter();
|
||||
console.log("Database_Contextual_View:onload", Date.now() - t, allEvents.length);
|
||||
|
||||
const initialEvents = [];
|
||||
for (const e of allEvents) {
|
||||
const pubkey = PublicKey.FromHex(e.pubkey);
|
||||
if (pubkey instanceof Error) {
|
||||
console.error("impossible state");
|
||||
await eventsAdapter.remove(e.id);
|
||||
continue;
|
||||
}
|
||||
const p: Parsed_Event = {
|
||||
...e,
|
||||
parsedTags: getTags(e),
|
||||
publicKey: pubkey,
|
||||
};
|
||||
initialEvents.push(p);
|
||||
}
|
||||
|
||||
console.log("Database_Contextual_View:parsed", Date.now() - t);
|
||||
|
||||
// Construct the View
|
||||
const db = new Database_Contextual_View(
|
||||
eventsAdapter,
|
||||
initialEvents,
|
||||
ctx,
|
||||
);
|
||||
console.log("Database_Contextual_View:New time spent", Date.now() - t);
|
||||
for (const e of db.events) {
|
||||
if (e.kind == NostrKind.META_DATA) {
|
||||
// @ts-ignore
|
||||
const pEvent = parseProfileEvent(e);
|
||||
if (pEvent instanceof Error) {
|
||||
return pEvent;
|
||||
}
|
||||
db.setProfile(pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
get(keys: Indices): Parsed_Event | undefined {
|
||||
for (const e of this.events) {
|
||||
if (e.id == keys.id) {
|
||||
@ -118,86 +152,10 @@ export class Database_Contextual_View implements DirectMessageGetter, ProfileCon
|
||||
}
|
||||
}
|
||||
|
||||
static async New(eventsAdapter: EventsAdapter, ctx: NostrAccountContext) {
|
||||
const t = Date.now();
|
||||
const allEvents = await eventsAdapter.filter();
|
||||
console.log("Database_Contextual_View:onload", Date.now() - t, allEvents.length);
|
||||
|
||||
const initialEvents = [];
|
||||
for (const e of allEvents) {
|
||||
const pubkey = PublicKey.FromHex(e.pubkey);
|
||||
if (pubkey instanceof Error) {
|
||||
console.error("impossible state");
|
||||
await eventsAdapter.remove(e.id);
|
||||
continue;
|
||||
}
|
||||
const p: Parsed_Event = {
|
||||
...e,
|
||||
parsedTags: getTags(e),
|
||||
publicKey: pubkey,
|
||||
};
|
||||
initialEvents.push(p);
|
||||
}
|
||||
|
||||
console.log("Database_Contextual_View:parsed", Date.now() - t);
|
||||
|
||||
// Construct the View
|
||||
const db = new Database_Contextual_View(
|
||||
eventsAdapter,
|
||||
initialEvents,
|
||||
ctx,
|
||||
);
|
||||
console.log("Database_Contextual_View:New time spent", Date.now() - t);
|
||||
for (const e of db.events) {
|
||||
if (e.kind == NostrKind.META_DATA) {
|
||||
// @ts-ignore
|
||||
const pEvent = parseProfileEvent(e);
|
||||
if (pEvent instanceof Error) {
|
||||
return pEvent;
|
||||
}
|
||||
db.setProfile(pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// load decrypted DMs
|
||||
(async () => {
|
||||
for (const event of allEvents) {
|
||||
if (event.kind != NostrKind.DIRECT_MESSAGE) {
|
||||
continue;
|
||||
}
|
||||
const dmEvent = await parseDM(
|
||||
// @ts-ignore
|
||||
event,
|
||||
ctx,
|
||||
getTags(event),
|
||||
PublicKey.FromHex(event.pubkey),
|
||||
);
|
||||
if (dmEvent instanceof Error) {
|
||||
await eventsAdapter.remove(event.id);
|
||||
continue;
|
||||
}
|
||||
db.directed_messages.set(event.id, dmEvent);
|
||||
db.sourceOfChange.put(null); // to render once
|
||||
}
|
||||
})();
|
||||
return db;
|
||||
}
|
||||
|
||||
public readonly filterEvents = (filter: (e: NostrEvent) => boolean) => {
|
||||
return this.events.filter(filter);
|
||||
};
|
||||
|
||||
// get the direct messages between me and this pubkey
|
||||
public getDirectMessages(pubkey: string) {
|
||||
const events = [];
|
||||
for (const event of this.directed_messages.values()) {
|
||||
if (is_DM_between(event, this.ctx.publicKey.hex, pubkey)) {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
return events.sort(compare);
|
||||
}
|
||||
|
||||
async addEvent(event: NostrEvent) {
|
||||
// check if the event exists
|
||||
const storedEvent = await this.eventsAdapter.get({ id: event.id });
|
||||
@ -227,14 +185,7 @@ export class Database_Contextual_View implements DirectMessageGetter, ProfileCon
|
||||
|
||||
this.events.push(parsedEvent);
|
||||
|
||||
if (parsedEvent.kind == NostrKind.DIRECT_MESSAGE) {
|
||||
// @ts-ignore
|
||||
const dmEvent = await parseDM(parsedEvent, this.ctx, parsedEvent.parsedTags, parsedEvent.pubkey);
|
||||
if (dmEvent instanceof Error) {
|
||||
return dmEvent;
|
||||
}
|
||||
this.directed_messages.set(parsedEvent.id, dmEvent);
|
||||
} else if (parsedEvent.kind == NostrKind.META_DATA) {
|
||||
if (parsedEvent.kind == NostrKind.META_DATA) {
|
||||
// @ts-ignore
|
||||
const pEvent = parseProfileEvent(parsedEvent);
|
||||
if (pEvent instanceof Error) {
|
||||
@ -253,7 +204,7 @@ export class Database_Contextual_View implements DirectMessageGetter, ProfileCon
|
||||
//////////////////
|
||||
subscribe() {
|
||||
const c = this.caster.copy();
|
||||
const res = csp.chan<Accepted_Event | null>(buffer_size);
|
||||
const res = csp.chan<Parsed_Event | null>(buffer_size);
|
||||
(async () => {
|
||||
for await (const newE of c) {
|
||||
const err = await res.put(newE);
|
||||
@ -356,13 +307,3 @@ export async function parseDM(
|
||||
parsedContentItems: Array.from(parseContent(decrypted)),
|
||||
};
|
||||
}
|
||||
|
||||
function is_DM_between(event: NostrEvent, myPubkey: string, theirPubKey: string) {
|
||||
if (event.pubkey == myPubkey) {
|
||||
return getTags(event).p[0] == theirPubKey;
|
||||
} else if (event.pubkey == theirPubKey) {
|
||||
return getTags(event).p[0] == myPubkey;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,18 @@
|
||||
import * as csp from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
||||
import { NostrAccountContext, NostrEvent, NostrKind } from "../lib/nostr-ts/nostr.ts";
|
||||
import { ConnectionPool } from "../lib/nostr-ts/relay.ts";
|
||||
import { prepareNostrImageEvent, Tag } from "../nostr.ts";
|
||||
import {
|
||||
compare,
|
||||
DirectedMessage_Event,
|
||||
getTags,
|
||||
Parsed_Event,
|
||||
prepareNostrImageEvent,
|
||||
Tag,
|
||||
} from "../nostr.ts";
|
||||
import { PublicKey } from "../lib/nostr-ts/key.ts";
|
||||
import { prepareEncryptedNostrEvent, prepareNormalNostrEvent } from "../lib/nostr-ts/event.ts";
|
||||
import { prepareEncryptedNostrEvent } from "../lib/nostr-ts/event.ts";
|
||||
import { DirectMessageGetter } from "../UI/app_update.tsx";
|
||||
import { parseDM } from "../database.ts";
|
||||
|
||||
export async function sendDMandImages(args: {
|
||||
sender: NostrAccountContext;
|
||||
@ -63,26 +72,6 @@ export async function sendDMandImages(args: {
|
||||
return eventsToSend;
|
||||
}
|
||||
|
||||
export async function sendSocialPost(args: {
|
||||
sender: NostrAccountContext;
|
||||
message: string;
|
||||
lamport_timestamp: number;
|
||||
pool: ConnectionPool;
|
||||
tags: Tag[];
|
||||
}) {
|
||||
const { sender, message, lamport_timestamp, pool, tags } = args;
|
||||
console.log("sendSocialPost", message);
|
||||
const event = await prepareNormalNostrEvent(sender, NostrKind.TEXT_NOTE, [
|
||||
["lamport", String(lamport_timestamp)],
|
||||
...tags,
|
||||
], message);
|
||||
const err = await pool.sendEvent(event);
|
||||
if (err instanceof Error) {
|
||||
return err;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
export function getAllEncryptedMessagesOf(
|
||||
publicKey: PublicKey,
|
||||
relay: ConnectionPool,
|
||||
@ -164,3 +153,45 @@ function merge<T>(...iters: AsyncIterable<T>[]) {
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
export class DirectedMessageController implements DirectMessageGetter {
|
||||
constructor(
|
||||
public readonly ctx: NostrAccountContext,
|
||||
) {}
|
||||
|
||||
public readonly directed_messages = new Map<string, DirectedMessage_Event>();
|
||||
|
||||
// get the direct messages between me and this pubkey
|
||||
public getDirectMessages(pubkey: string) {
|
||||
const events = [];
|
||||
for (const event of this.directed_messages.values()) {
|
||||
if (is_DM_between(event, this.ctx.publicKey.hex, pubkey)) {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
return events.sort(compare);
|
||||
}
|
||||
|
||||
async addEvent(event: Parsed_Event<NostrKind.DIRECT_MESSAGE>) {
|
||||
const dmEvent = await parseDM(
|
||||
event,
|
||||
this.ctx,
|
||||
event.parsedTags,
|
||||
event.publicKey,
|
||||
);
|
||||
if (dmEvent instanceof Error) {
|
||||
return dmEvent;
|
||||
}
|
||||
this.directed_messages.set(event.id, dmEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function is_DM_between(event: NostrEvent, myPubkey: string, theirPubKey: string) {
|
||||
if (event.pubkey == myPubkey) {
|
||||
return getTags(event).p[0] == theirPubKey;
|
||||
} else if (event.pubkey == theirPubKey) {
|
||||
return getTags(event).p[0] == myPubkey;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { assertEquals, fail } from "https://deno.land/std@0.176.0/testing/asserts.ts";
|
||||
import { prepareEncryptedNostrEvent } from "./lib/nostr-ts/event.ts";
|
||||
import { PrivateKey } from "./lib/nostr-ts/key.ts";
|
||||
import { InMemoryAccountContext, RelayResponse_REQ_Message } from "./lib/nostr-ts/nostr.ts";
|
||||
import { Channel, closed } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
||||
import { ConnectionPool } from "./lib/nostr-ts/relay.ts";
|
||||
import { relays } from "./lib/nostr-ts/relay-list.test.ts";
|
||||
import { parseJSON } from "./features/profile.ts";
|
||||
import { prepareEncryptedNostrEvent } from "../lib/nostr-ts/event.ts";
|
||||
import { PrivateKey } from "../lib/nostr-ts/key.ts";
|
||||
import { InMemoryAccountContext, RelayResponse_REQ_Message } from "../lib/nostr-ts/nostr.ts";
|
||||
import { relays } from "../lib/nostr-ts/relay-list.test.ts";
|
||||
import { ConnectionPool } from "../lib/nostr-ts/relay.ts";
|
||||
import { parseJSON } from "./profile.ts";
|
||||
|
||||
Deno.test("group chat", async () => {
|
||||
const pool = new ConnectionPool();
|
@ -1,16 +1,16 @@
|
||||
import { z } from "https://esm.sh/zod@3.22.4";
|
||||
import { ConversationLists, ConversationSummary } from "./UI/conversation-list.ts";
|
||||
import { parseJSON, ProfileSyncer } from "./features/profile.ts";
|
||||
import { prepareEncryptedNostrEvent } from "./lib/nostr-ts/event.ts";
|
||||
import { PrivateKey, PublicKey } from "./lib/nostr-ts/key.ts";
|
||||
import { InMemoryAccountContext, NostrAccountContext, NostrEvent, NostrKind } from "./lib/nostr-ts/nostr.ts";
|
||||
import { GroupMessageGetter } from "./UI/app_update.tsx";
|
||||
import { getTags } from "./nostr.ts";
|
||||
import { ChatMessage } from "./UI/message.ts";
|
||||
import { GroupChatListGetter } from "./UI/conversation-list.tsx";
|
||||
import { Channel, semaphore } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
||||
import { ConnectionPool } from "./lib/nostr-ts/relay.ts";
|
||||
import { Database_Contextual_View } from "./database.ts";
|
||||
import { GroupMessageGetter } from "../UI/app_update.tsx";
|
||||
import { ConversationLists, ConversationSummary } from "../UI/conversation-list.ts";
|
||||
import { GroupMessageListGetter } from "../UI/conversation-list.tsx";
|
||||
import { ChatMessage } from "../UI/message.ts";
|
||||
import { Database_Contextual_View } from "../database.ts";
|
||||
import { prepareEncryptedNostrEvent } from "../lib/nostr-ts/event.ts";
|
||||
import { PrivateKey, PublicKey } from "../lib/nostr-ts/key.ts";
|
||||
import { InMemoryAccountContext, NostrAccountContext, NostrEvent, NostrKind } from "../lib/nostr-ts/nostr.ts";
|
||||
import { ConnectionPool } from "../lib/nostr-ts/relay.ts";
|
||||
import { getTags } from "../nostr.ts";
|
||||
import { parseJSON, ProfileSyncer } from "./profile.ts";
|
||||
|
||||
export type GM_Types = "gm_creation" | "gm_message" | "gm_invitation";
|
||||
|
||||
@ -28,7 +28,7 @@ export type GroupChatInvitation = {
|
||||
groupAddr: PublicKey;
|
||||
};
|
||||
|
||||
export class GroupChatController implements GroupMessageGetter, GroupChatListGetter {
|
||||
export class GroupMessageController implements GroupMessageGetter, GroupMessageListGetter {
|
||||
created_groups = new Map<string, GroupChatCreation>();
|
||||
invitations = new Map<string, GroupChatInvitation>();
|
||||
messages = new Map<string, ChatMessage[]>();
|
||||
@ -41,7 +41,7 @@ export class GroupChatController implements GroupMessageGetter, GroupChatListGet
|
||||
private readonly profileSyncer: ProfileSyncer,
|
||||
) {}
|
||||
|
||||
getGroupChat() {
|
||||
getConversationList() {
|
||||
const conversations: ConversationSummary[] = [];
|
||||
for (const v of this.created_groups.values()) {
|
||||
conversations.push({
|
||||
@ -103,7 +103,7 @@ export class GroupChatController implements GroupMessageGetter, GroupChatListGet
|
||||
} else if (type == "gm_invitation") { // I received
|
||||
return await this.handleInvitation(event);
|
||||
} else {
|
||||
console.log(GroupChatController.name, "ignore", event, "type", type);
|
||||
console.log(GroupMessageController.name, "ignore", event, "type", type);
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
|
||||
Nostr support public group chat in https://github.com/nostr-protocol/nips/blob/master/28.md
|
||||
It's globally public therefore no encryption
|
||||
|
||||
This module experiments with an encrypted private group chat
|
||||
|
||||
Basic design:
|
||||
A, B, C are 3 users who want to chat together.
|
||||
A sends a DM to [B, C] with a special marker in the content to signal the client
|
||||
that this is a group chat.
|
||||
|
||||
content: {
|
||||
message: string
|
||||
|
||||
,
|
||||
group_chat_id: string
|
||||
}
|
||||
|
||||
*/
|
Loading…
Reference in New Issue
Block a user