2023-09-10 17:56:37 +00:00
|
|
|
/** @jsx h */
|
2024-03-15 13:44:17 +00:00
|
|
|
import { ComponentChildren, h } from "https://esm.sh/preact@10.17.1";
|
2024-03-17 09:00:07 +00:00
|
|
|
import {
|
|
|
|
Channel,
|
|
|
|
closed,
|
|
|
|
PopChannel,
|
|
|
|
PutChannel,
|
|
|
|
sleep,
|
|
|
|
} from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
2024-03-15 15:46:11 +00:00
|
|
|
import { prepareNormalNostrEvent } from "../../libs/nostr.ts/event.ts";
|
2024-01-01 17:28:10 +00:00
|
|
|
import { PublicKey } from "../../libs/nostr.ts/key.ts";
|
|
|
|
import { NoteID } from "../../libs/nostr.ts/nip19.ts";
|
|
|
|
import { NostrAccountContext, NostrEvent, NostrKind } from "../../libs/nostr.ts/nostr.ts";
|
|
|
|
import { ConnectionPool } from "../../libs/nostr.ts/relay-pool.ts";
|
2024-03-15 13:44:17 +00:00
|
|
|
import { Database_View } from "../database.ts";
|
2023-10-15 22:39:21 +00:00
|
|
|
import { emitFunc, EventBus } from "../event-bus.ts";
|
2024-03-15 13:44:17 +00:00
|
|
|
import { DirectedMessageController, sendDirectMessages } from "../features/dm.ts";
|
|
|
|
import { saveProfile } from "../features/profile.ts";
|
2023-07-15 16:40:21 +00:00
|
|
|
import {
|
2023-09-12 14:51:27 +00:00
|
|
|
Encrypted_Event,
|
2023-07-15 16:40:21 +00:00
|
|
|
getTags,
|
2023-10-09 18:39:52 +00:00
|
|
|
Parsed_Event,
|
2023-10-01 22:21:55 +00:00
|
|
|
PinConversation,
|
2023-07-15 16:40:21 +00:00
|
|
|
Profile_Nostr_Event,
|
2023-10-01 22:21:55 +00:00
|
|
|
UnpinConversation,
|
2023-07-15 16:40:21 +00:00
|
|
|
} from "../nostr.ts";
|
2024-01-01 17:28:10 +00:00
|
|
|
import { LamportTime } from "../time.ts";
|
|
|
|
import { App } from "./app.tsx";
|
|
|
|
import { Model } from "./app_model.ts";
|
2023-09-10 17:56:37 +00:00
|
|
|
import { PopOverInputChannel } from "./components/popover.tsx";
|
2023-10-01 23:16:08 +00:00
|
|
|
import { OtherConfig } from "./config-other.ts";
|
2024-01-01 17:28:10 +00:00
|
|
|
import { DM_List } from "./conversation-list.ts";
|
|
|
|
import { ContactUpdate } from "./conversation-list.tsx";
|
|
|
|
import { StartInvite } from "./dm.tsx";
|
2023-10-23 13:57:58 +00:00
|
|
|
import { SaveProfile } from "./edit-profile.tsx";
|
2024-03-15 13:44:17 +00:00
|
|
|
import { EditorEvent, SendMessage } from "./editor.tsx";
|
2024-01-01 17:28:10 +00:00
|
|
|
import { EventDetail, EventDetailItem } from "./event-detail.tsx";
|
2024-03-15 13:44:17 +00:00
|
|
|
|
2024-01-01 17:28:10 +00:00
|
|
|
import { DirectMessagePanelUpdate } from "./message-panel.tsx";
|
|
|
|
import { ChatMessage } from "./message.ts";
|
2024-03-15 13:44:17 +00:00
|
|
|
import { InstallPrompt, NavigationModel, NavigationUpdate, SelectRelay } from "./nav.tsx";
|
2024-01-01 17:28:10 +00:00
|
|
|
import { notify } from "./notification.ts";
|
2024-03-15 13:44:17 +00:00
|
|
|
import { RelayInformationComponent } from "./relay-detail.tsx";
|
2024-01-01 17:28:10 +00:00
|
|
|
import { Search } from "./search.tsx";
|
|
|
|
import { SearchUpdate, SelectConversation } from "./search_model.ts";
|
2024-03-15 13:44:17 +00:00
|
|
|
import { RelayConfigChange, ViewRecommendedRelaysList, ViewRelayDetail } from "./setting.tsx";
|
|
|
|
import { SignInEvent } from "./sign-in.ts";
|
2024-01-02 10:09:25 +00:00
|
|
|
import { TagSelected } from "./contact-tags.tsx";
|
2024-03-15 13:44:17 +00:00
|
|
|
import { BlockUser, UnblockUser, UserDetail } from "./user-detail.tsx";
|
|
|
|
import { RelayRecommendList } from "./relay-recommend-list.tsx";
|
|
|
|
import { HidePopOver } from "./components/popover.tsx";
|
|
|
|
import { Social_Model } from "./channel-container.tsx";
|
|
|
|
import { SyncEvent } from "./message-panel.tsx";
|
2024-03-15 15:18:36 +00:00
|
|
|
import { SendingEventRejection, ToastChannel } from "./components/toast.tsx";
|
2024-03-15 15:46:11 +00:00
|
|
|
import { SingleRelayConnection } from "../../libs/nostr.ts/relay-single.ts";
|
|
|
|
import { default_blowater_relay } from "./relay-config.ts";
|
2024-03-17 09:00:07 +00:00
|
|
|
import { forever } from "./_helper.ts";
|
2024-03-19 10:27:18 +00:00
|
|
|
import { generateTags } from "./editor.ts";
|
2023-06-30 14:05:57 +00:00
|
|
|
|
|
|
|
export type UI_Interaction_Event =
|
|
|
|
| SearchUpdate
|
|
|
|
| ContactUpdate
|
|
|
|
| EditorEvent
|
|
|
|
| NavigationUpdate
|
|
|
|
| DirectMessagePanelUpdate
|
2024-03-15 13:44:17 +00:00
|
|
|
| BackToChannelList
|
2023-06-30 14:05:57 +00:00
|
|
|
| BackToContactList
|
2023-10-23 13:57:58 +00:00
|
|
|
| SaveProfile
|
2023-10-01 22:21:55 +00:00
|
|
|
| PinConversation
|
|
|
|
| UnpinConversation
|
2023-07-27 08:51:53 +00:00
|
|
|
| SignInEvent
|
2023-09-22 16:27:36 +00:00
|
|
|
| RelayConfigChange
|
2023-10-19 03:44:43 +00:00
|
|
|
| StartInvite
|
2024-01-02 10:09:25 +00:00
|
|
|
| ViewRelayDetail
|
2024-03-15 13:44:17 +00:00
|
|
|
| ViewRecommendedRelaysList
|
2024-01-02 12:42:11 +00:00
|
|
|
| TagSelected
|
|
|
|
| BlockUser
|
2024-03-15 13:44:17 +00:00
|
|
|
| UnblockUser
|
|
|
|
| SelectRelay
|
|
|
|
| HidePopOver
|
|
|
|
| SyncEvent;
|
2023-06-30 14:05:57 +00:00
|
|
|
|
2024-03-15 13:44:17 +00:00
|
|
|
type BackToChannelList = {
|
|
|
|
type: "BackToChannelList";
|
|
|
|
};
|
2023-06-30 14:05:57 +00:00
|
|
|
type BackToContactList = {
|
|
|
|
type: "BackToContactList";
|
|
|
|
};
|
|
|
|
export type AppEventBus = EventBus<UI_Interaction_Event>;
|
|
|
|
|
2024-01-02 12:42:11 +00:00
|
|
|
export type UserBlocker = {
|
|
|
|
blockUser(pubkey: PublicKey): void;
|
|
|
|
unblockUser(pubkey: PublicKey): void;
|
|
|
|
isUserBlocked(pubkey: PublicKey): boolean;
|
|
|
|
getBlockedUsers(): Set<string>;
|
|
|
|
};
|
|
|
|
|
2023-06-30 14:05:57 +00:00
|
|
|
/////////////////////
|
|
|
|
// UI Interfaction //
|
|
|
|
/////////////////////
|
2024-03-17 09:00:07 +00:00
|
|
|
export function UI_Interaction_Update(args: {
|
|
|
|
model: Model;
|
|
|
|
eventBus: AppEventBus;
|
|
|
|
dbView: Database_View;
|
|
|
|
pool: ConnectionPool;
|
|
|
|
popOver: PopOverInputChannel;
|
|
|
|
rightPanel: Channel<() => ComponentChildren>;
|
|
|
|
newNostrEventChannel: Channel<NostrEvent>;
|
|
|
|
lamport: LamportTime;
|
|
|
|
installPrompt: InstallPrompt;
|
|
|
|
toastInputChan: ToastChannel;
|
|
|
|
}): Channel<true> {
|
|
|
|
const chan = new Channel<true>();
|
|
|
|
forever(handle_update_event(chan, args));
|
|
|
|
return chan;
|
|
|
|
}
|
|
|
|
|
|
|
|
const handle_update_event = async (chan: PutChannel<true>, args: {
|
2023-08-03 09:13:16 +00:00
|
|
|
model: Model;
|
|
|
|
eventBus: AppEventBus;
|
2024-03-15 13:44:17 +00:00
|
|
|
dbView: Database_View;
|
2023-08-03 09:13:16 +00:00
|
|
|
pool: ConnectionPool;
|
2023-09-10 17:56:37 +00:00
|
|
|
popOver: PopOverInputChannel;
|
2024-03-15 13:44:17 +00:00
|
|
|
rightPanel: Channel<() => ComponentChildren>;
|
2023-11-25 13:26:03 +00:00
|
|
|
newNostrEventChannel: Channel<NostrEvent>;
|
2023-11-25 14:29:50 +00:00
|
|
|
lamport: LamportTime;
|
2023-11-27 13:35:01 +00:00
|
|
|
installPrompt: InstallPrompt;
|
2024-03-15 14:57:19 +00:00
|
|
|
toastInputChan: ToastChannel;
|
2024-03-17 09:00:07 +00:00
|
|
|
}) => {
|
2023-11-27 13:35:01 +00:00
|
|
|
const { model, dbView, eventBus, pool, installPrompt } = args;
|
2024-03-15 13:44:17 +00:00
|
|
|
for await (const event of eventBus.onChange()) {
|
2023-06-30 14:05:57 +00:00
|
|
|
console.log(event);
|
2024-03-15 15:46:11 +00:00
|
|
|
if (event.type == "SignInEvent") {
|
|
|
|
const ctx = event.ctx;
|
|
|
|
if (ctx) {
|
|
|
|
console.log("sign in as", ctx.publicKey.bech32());
|
|
|
|
const otherConfig = await OtherConfig.FromLocalStorage(
|
|
|
|
ctx,
|
|
|
|
args.newNostrEventChannel,
|
|
|
|
args.lamport,
|
|
|
|
);
|
|
|
|
const app = await App.Start({
|
|
|
|
database: dbView,
|
|
|
|
model,
|
|
|
|
ctx,
|
|
|
|
eventBus,
|
|
|
|
pool,
|
|
|
|
popOverInputChan: args.popOver,
|
|
|
|
rightPanelInputChan: args.rightPanel,
|
|
|
|
otherConfig,
|
|
|
|
lamport: args.lamport,
|
|
|
|
installPrompt,
|
|
|
|
toastInputChan: args.toastInputChan,
|
|
|
|
});
|
|
|
|
model.app = app;
|
|
|
|
} else {
|
|
|
|
console.error("failed to sign in");
|
|
|
|
}
|
|
|
|
continue;
|
2023-07-09 07:06:13 +00:00
|
|
|
}
|
|
|
|
|
2023-09-10 17:56:37 +00:00
|
|
|
const app = model.app;
|
|
|
|
if (app == undefined) { // if not signed in
|
2023-07-11 10:45:57 +00:00
|
|
|
console.warn(event, "is not valid before signing");
|
|
|
|
console.warn("This could not happen!");
|
|
|
|
continue;
|
2024-03-15 15:46:11 +00:00
|
|
|
}
|
|
|
|
|
2024-03-15 14:30:58 +00:00
|
|
|
const current_relay = pool.getRelay(model.currentRelay);
|
|
|
|
if (current_relay == undefined) {
|
|
|
|
console.error(Array.from(pool.getRelays()));
|
|
|
|
continue;
|
2024-03-15 15:46:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const blowater_relay = pool.getRelay(default_blowater_relay);
|
|
|
|
if (blowater_relay == undefined) {
|
|
|
|
console.error(Array.from(pool.getRelays()));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// All events below are only valid after signning in
|
|
|
|
if (event.type == "SelectRelay") {
|
2024-03-15 13:44:17 +00:00
|
|
|
model.currentRelay = event.relay.url;
|
|
|
|
} //
|
2023-09-24 21:56:29 +00:00
|
|
|
// Searchx
|
2023-06-30 14:05:57 +00:00
|
|
|
//
|
2024-03-15 13:44:17 +00:00
|
|
|
else if (event.type == "HidePopOver") {
|
|
|
|
app.popOverInputChan.put({
|
|
|
|
children: undefined,
|
|
|
|
});
|
2023-06-30 14:05:57 +00:00
|
|
|
} else if (event.type == "StartSearch") {
|
2024-03-15 13:44:17 +00:00
|
|
|
app.database.getProfilesByText;
|
2023-09-10 17:56:37 +00:00
|
|
|
const search = (
|
|
|
|
<Search
|
2024-03-15 13:44:17 +00:00
|
|
|
placeholder={`Search a user's public key or name (${app.database.getUniqueProfileCount()} profiles)`}
|
2024-03-16 07:09:30 +00:00
|
|
|
profileGetter={app.database}
|
2023-09-10 17:56:37 +00:00
|
|
|
emit={eventBus.emit}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
args.popOver.put({ children: search });
|
2023-06-30 14:05:57 +00:00
|
|
|
} //
|
|
|
|
//
|
2023-11-21 07:20:39 +00:00
|
|
|
// Setting
|
|
|
|
//
|
|
|
|
else if (event.type == "ViewRelayDetail") {
|
|
|
|
app.popOverInputChan.put({
|
2024-03-17 07:07:42 +00:00
|
|
|
children: (
|
|
|
|
<RelayInformationComponent
|
|
|
|
relayUrl={event.url}
|
|
|
|
profileGetter={app.database}
|
|
|
|
emit={app.eventBus.emit}
|
|
|
|
/>
|
|
|
|
),
|
2024-03-15 13:44:17 +00:00
|
|
|
});
|
|
|
|
} else if (event.type == "ViewRecommendedRelaysList") {
|
|
|
|
app.popOverInputChan.put({
|
|
|
|
children: (
|
|
|
|
<RelayRecommendList
|
2024-03-16 13:01:10 +00:00
|
|
|
relayConfig={app.relayConfig}
|
2024-03-15 13:44:17 +00:00
|
|
|
emit={eventBus.emit}
|
|
|
|
/>
|
|
|
|
),
|
2023-11-21 07:20:39 +00:00
|
|
|
});
|
|
|
|
} //
|
|
|
|
//
|
2023-06-30 14:05:57 +00:00
|
|
|
// Contacts
|
|
|
|
//
|
2023-09-23 21:33:30 +00:00
|
|
|
else if (event.type == "SelectConversation") {
|
2024-03-15 13:44:17 +00:00
|
|
|
console.log("SelectConversation", event.pubkey.hex);
|
2023-12-17 20:41:49 +00:00
|
|
|
model.navigationModel.activeNav = "DM";
|
2024-03-15 13:44:17 +00:00
|
|
|
model.dm.currentConversation = event.pubkey;
|
2023-09-10 17:56:37 +00:00
|
|
|
app.popOverInputChan.put({ children: undefined });
|
2024-03-15 13:44:17 +00:00
|
|
|
app.conversationLists.markRead(event.pubkey);
|
2023-06-30 14:05:57 +00:00
|
|
|
} else if (event.type == "BackToContactList") {
|
2024-03-15 13:44:17 +00:00
|
|
|
model.dm.currentConversation = undefined;
|
2023-10-01 22:21:55 +00:00
|
|
|
} else if (event.type == "PinConversation") {
|
2023-11-25 13:26:03 +00:00
|
|
|
const err1 = await app.otherConfig.addPin(event.pubkey);
|
|
|
|
if (err1 instanceof Error) {
|
|
|
|
console.error(err1);
|
2023-10-01 22:21:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if (event.type == "UnpinConversation") {
|
2023-11-25 13:26:03 +00:00
|
|
|
const err1 = await app.otherConfig.removePin(event.pubkey);
|
|
|
|
if (err1 instanceof Error) {
|
|
|
|
console.error(err1);
|
2023-10-01 22:21:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
2023-06-30 14:05:57 +00:00
|
|
|
} //
|
|
|
|
//
|
|
|
|
// Editor
|
|
|
|
//
|
|
|
|
else if (event.type == "SendMessage") {
|
2023-12-04 18:14:26 +00:00
|
|
|
handle_SendMessage(
|
2023-09-11 18:09:38 +00:00
|
|
|
event,
|
2023-09-15 08:58:40 +00:00
|
|
|
app.ctx,
|
2023-09-11 18:09:38 +00:00
|
|
|
app.lamport,
|
|
|
|
app.database,
|
2024-03-15 15:46:11 +00:00
|
|
|
{
|
|
|
|
...model,
|
|
|
|
current_relay,
|
|
|
|
blowater_relay,
|
|
|
|
},
|
2023-12-04 18:14:26 +00:00
|
|
|
).then((res) => {
|
|
|
|
if (res instanceof Error) {
|
2024-03-15 14:57:19 +00:00
|
|
|
console.error(res);
|
2024-03-15 15:18:36 +00:00
|
|
|
app.toastInputChan.put(
|
|
|
|
SendingEventRejection(eventBus.emit, current_relay.url, res.message),
|
|
|
|
);
|
2024-03-17 09:00:07 +00:00
|
|
|
} else {
|
|
|
|
chan.put(true);
|
2023-12-04 18:14:26 +00:00
|
|
|
}
|
|
|
|
});
|
2023-06-30 14:05:57 +00:00
|
|
|
} else if (event.type == "UpdateMessageFiles") {
|
2024-03-15 13:44:17 +00:00
|
|
|
console.log("to be implemented");
|
2023-10-07 10:59:06 +00:00
|
|
|
} else if (event.type == "UpdateEditorText") {
|
2024-03-15 13:44:17 +00:00
|
|
|
console.log("to be implemented");
|
2023-06-30 14:05:57 +00:00
|
|
|
} //
|
|
|
|
//
|
2023-10-23 13:57:58 +00:00
|
|
|
// Profile
|
2023-06-30 14:05:57 +00:00
|
|
|
//
|
2023-10-23 13:57:58 +00:00
|
|
|
else if (event.type == "SaveProfile") {
|
2024-03-17 17:05:32 +00:00
|
|
|
if (event.profile == undefined) {
|
2024-03-15 15:18:36 +00:00
|
|
|
app.toastInputChan.put(() => "profile is empty");
|
2024-03-15 14:30:58 +00:00
|
|
|
} else {
|
2024-03-17 17:05:32 +00:00
|
|
|
saveProfile(
|
2024-03-15 14:30:58 +00:00
|
|
|
event.profile,
|
|
|
|
event.ctx,
|
|
|
|
current_relay,
|
2024-03-17 17:05:32 +00:00
|
|
|
).then((result) => {
|
|
|
|
app.popOverInputChan.put({ children: undefined });
|
|
|
|
if (result instanceof Error) {
|
|
|
|
app.toastInputChan.put(
|
|
|
|
SendingEventRejection(eventBus.emit, current_relay.url, result.message),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
app.toastInputChan.put(() => "profile has been updated");
|
|
|
|
}
|
|
|
|
});
|
2024-03-15 14:30:58 +00:00
|
|
|
}
|
2023-06-30 14:05:57 +00:00
|
|
|
} //
|
|
|
|
//
|
|
|
|
// Navigation
|
|
|
|
//
|
|
|
|
else if (event.type == "ChangeNavigation") {
|
2023-11-02 13:10:16 +00:00
|
|
|
model.navigationModel.activeNav = event.id;
|
2023-06-30 14:05:57 +00:00
|
|
|
} //
|
|
|
|
//
|
2024-03-15 13:44:17 +00:00
|
|
|
// Channel
|
2023-06-30 14:05:57 +00:00
|
|
|
//
|
2024-03-15 13:44:17 +00:00
|
|
|
else if (event.type == "SelectChannel") {
|
|
|
|
model.social.relaySelectedChannel.set(model.currentRelay, event.channel);
|
|
|
|
app.popOverInputChan.put({ children: undefined });
|
|
|
|
} else if (event.type == "BackToChannelList") {
|
|
|
|
model.social.relaySelectedChannel.delete(model.currentRelay);
|
|
|
|
app.popOverInputChan.put({ children: undefined });
|
|
|
|
} //
|
|
|
|
// DM
|
|
|
|
//
|
|
|
|
else if (event.type == "ViewUserDetail") {
|
|
|
|
app.rightPanelInputChan.put(
|
|
|
|
() => {
|
|
|
|
return (
|
|
|
|
<UserDetail
|
2024-03-17 09:00:07 +00:00
|
|
|
targetUserProfile={app.database.getProfilesByPublicKey(event.pubkey)
|
|
|
|
?.profile ||
|
2024-03-15 13:44:17 +00:00
|
|
|
{}}
|
|
|
|
pubkey={event.pubkey}
|
|
|
|
emit={eventBus.emit}
|
|
|
|
// dmList={app.conversationLists}
|
|
|
|
blocked={app.conversationLists.isUserBlocked(event.pubkey)}
|
|
|
|
/>
|
|
|
|
);
|
2023-10-26 08:42:04 +00:00
|
|
|
},
|
2023-09-24 21:56:29 +00:00
|
|
|
);
|
2024-03-15 13:44:17 +00:00
|
|
|
} else if (event.type == "OpenNote") {
|
|
|
|
open(`https://nostrapp.link/#${NoteID.FromHex(event.event.id).bech32()}?select=true`);
|
2023-10-07 17:15:27 +00:00
|
|
|
} else if (event.type == "StartInvite") {
|
|
|
|
app.popOverInputChan.put({
|
|
|
|
children: <div></div>,
|
|
|
|
});
|
2023-09-06 21:04:25 +00:00
|
|
|
} else if (event.type == "RelayConfigChange") {
|
2024-03-16 13:01:10 +00:00
|
|
|
(async () => {
|
|
|
|
if (event.kind == "add") {
|
2024-03-16 13:29:04 +00:00
|
|
|
const relay = await app.relayConfig.add(event.url);
|
|
|
|
if (relay instanceof Error) {
|
|
|
|
console.error(relay);
|
|
|
|
const msg = relay.message;
|
|
|
|
app.toastInputChan.put(() => msg);
|
|
|
|
}
|
2024-03-16 13:01:10 +00:00
|
|
|
} else {
|
2024-03-16 13:29:04 +00:00
|
|
|
const err = await app.relayConfig.remove(event.url);
|
|
|
|
if (err instanceof Error) {
|
|
|
|
app.toastInputChan.put(() => err.message);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (current_relay.url == event.url) {
|
|
|
|
model.currentRelay = default_blowater_relay;
|
|
|
|
}
|
2024-03-16 13:01:10 +00:00
|
|
|
}
|
|
|
|
})();
|
2023-09-10 17:56:37 +00:00
|
|
|
} else if (event.type == "ViewEventDetail") {
|
2023-10-07 23:23:53 +00:00
|
|
|
const nostrEvent = event.message.event;
|
2023-09-10 17:56:37 +00:00
|
|
|
const eventID = nostrEvent.id;
|
|
|
|
const eventIDBech32 = NoteID.FromString(nostrEvent.id).bech32();
|
2023-10-07 23:23:53 +00:00
|
|
|
const authorPubkey = event.message.author;
|
|
|
|
|
2023-09-10 17:56:37 +00:00
|
|
|
const content = nostrEvent.content;
|
|
|
|
const originalEventRaw = JSON.stringify(
|
|
|
|
{
|
|
|
|
content: nostrEvent.content,
|
|
|
|
created_at: nostrEvent.created_at,
|
|
|
|
kind: nostrEvent.kind,
|
|
|
|
tags: nostrEvent.tags,
|
|
|
|
pubkey: nostrEvent.pubkey,
|
|
|
|
id: nostrEvent.id,
|
|
|
|
sig: nostrEvent.sig,
|
|
|
|
},
|
|
|
|
null,
|
|
|
|
4,
|
|
|
|
);
|
|
|
|
|
|
|
|
const items: EventDetailItem[] = [
|
|
|
|
{
|
|
|
|
title: "Event ID",
|
|
|
|
fields: [
|
|
|
|
eventID,
|
|
|
|
eventIDBech32,
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
title: "Author",
|
|
|
|
fields: [
|
2023-10-07 23:23:53 +00:00
|
|
|
authorPubkey.hex,
|
|
|
|
authorPubkey.bech32(),
|
2023-09-10 17:56:37 +00:00
|
|
|
],
|
|
|
|
},
|
2023-11-16 13:22:18 +00:00
|
|
|
{
|
|
|
|
title: "Relays",
|
2023-11-20 03:59:59 +00:00
|
|
|
fields: Array.from(app.database.getRelayRecord(nostrEvent.id)),
|
2023-11-16 13:22:18 +00:00
|
|
|
},
|
2024-03-16 11:22:46 +00:00
|
|
|
];
|
|
|
|
if (nostrEvent.kind == NostrKind.DIRECT_MESSAGE) {
|
|
|
|
items.push({
|
2023-09-10 17:56:37 +00:00
|
|
|
title: "Content",
|
|
|
|
fields: [
|
|
|
|
content,
|
2023-10-21 11:29:47 +00:00
|
|
|
event.message.content,
|
2023-09-10 17:56:37 +00:00
|
|
|
originalEventRaw,
|
|
|
|
],
|
2024-03-16 11:22:46 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
items.push({
|
|
|
|
title: "Content",
|
|
|
|
fields: [
|
|
|
|
event.message.content,
|
|
|
|
originalEventRaw,
|
|
|
|
],
|
|
|
|
});
|
|
|
|
}
|
2023-09-10 17:56:37 +00:00
|
|
|
app.popOverInputChan.put({
|
|
|
|
children: (
|
|
|
|
<EventDetail
|
|
|
|
items={items}
|
|
|
|
/>
|
|
|
|
),
|
|
|
|
});
|
2024-01-02 12:42:11 +00:00
|
|
|
} else if (event.type == "BlockUser") {
|
|
|
|
app.conversationLists.blockUser(event.pubkey);
|
|
|
|
} else if (event.type == "UnblockUser") {
|
|
|
|
app.conversationLists.unblockUser(event.pubkey);
|
2024-03-15 13:44:17 +00:00
|
|
|
} else if (event.type == "SyncEvent") {
|
|
|
|
for (const relay of app.pool.getRelays()) {
|
|
|
|
relay.getEvent(event.eventID).then((nostr_event) => {
|
|
|
|
if (nostr_event instanceof Error) {
|
|
|
|
console.error(nostr_event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (nostr_event) {
|
|
|
|
app.database.addEvent(nostr_event, relay.url);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
console.log(event, "is not handled");
|
|
|
|
continue;
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
2024-03-17 09:00:07 +00:00
|
|
|
await chan.put(true);
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
2024-03-17 09:00:07 +00:00
|
|
|
};
|
2023-06-30 14:05:57 +00:00
|
|
|
|
2023-11-20 09:14:17 +00:00
|
|
|
export type DirectMessageGetter = ChatMessagesGetter & {
|
|
|
|
getDirectMessageStream(publicKey: string): Channel<ChatMessage>;
|
2023-09-23 20:53:40 +00:00
|
|
|
};
|
|
|
|
|
2023-11-20 09:14:17 +00:00
|
|
|
export type ChatMessagesGetter = {
|
|
|
|
getChatMessages(publicKey: string): ChatMessage[];
|
2023-10-07 23:23:53 +00:00
|
|
|
};
|
|
|
|
|
2023-06-30 14:05:57 +00:00
|
|
|
//////////////
|
|
|
|
// Database //
|
|
|
|
//////////////
|
|
|
|
export async function* Database_Update(
|
|
|
|
ctx: NostrAccountContext,
|
2024-03-15 13:44:17 +00:00
|
|
|
database: Database_View,
|
2023-06-30 14:05:57 +00:00
|
|
|
model: Model,
|
|
|
|
lamport: LamportTime,
|
2023-10-15 22:39:21 +00:00
|
|
|
convoLists: DM_List,
|
2023-10-09 18:39:52 +00:00
|
|
|
dmController: DirectedMessageController,
|
2023-10-15 22:39:21 +00:00
|
|
|
emit: emitFunc<SelectConversation>,
|
2023-11-25 13:26:03 +00:00
|
|
|
args: {
|
|
|
|
otherConfig: OtherConfig;
|
|
|
|
},
|
2023-06-30 14:05:57 +00:00
|
|
|
) {
|
2023-09-23 20:53:40 +00:00
|
|
|
const changes = database.subscribe();
|
2023-06-30 14:05:57 +00:00
|
|
|
while (true) {
|
2024-01-01 17:28:10 +00:00
|
|
|
await sleep(333);
|
2023-06-30 14:05:57 +00:00
|
|
|
await changes.ready();
|
2023-10-09 18:39:52 +00:00
|
|
|
const changes_events: (Encrypted_Event | Profile_Nostr_Event | Parsed_Event)[] = [];
|
2023-06-30 14:05:57 +00:00
|
|
|
while (true) {
|
|
|
|
if (!changes.isReadyToPop()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const e = await changes.pop();
|
2024-01-01 17:28:10 +00:00
|
|
|
if (e == closed) {
|
2023-06-30 14:05:57 +00:00
|
|
|
console.error("unreachable: db changes channel should never close");
|
|
|
|
break;
|
|
|
|
}
|
2024-03-17 09:00:07 +00:00
|
|
|
changes_events.push(e.event);
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-22 13:03:59 +00:00
|
|
|
convoLists.addEvents(changes_events, true);
|
2023-06-30 14:05:57 +00:00
|
|
|
for (let e of changes_events) {
|
|
|
|
const t = getTags(e).lamport_timestamp;
|
|
|
|
if (t) {
|
|
|
|
lamport.set(t);
|
|
|
|
}
|
|
|
|
if (e.kind == NostrKind.META_DATA || e.kind == NostrKind.DIRECT_MESSAGE) {
|
2024-03-17 17:05:32 +00:00
|
|
|
if (e.kind == NostrKind.DIRECT_MESSAGE) {
|
2024-03-15 13:44:17 +00:00
|
|
|
console.log("add event");
|
2023-10-09 18:39:52 +00:00
|
|
|
const err = await dmController.addEvent({
|
|
|
|
...e,
|
|
|
|
kind: e.kind,
|
|
|
|
});
|
|
|
|
if (err instanceof Error) {
|
|
|
|
console.error(err);
|
2023-07-04 06:37:14 +00:00
|
|
|
}
|
2024-03-15 13:44:17 +00:00
|
|
|
console.log("add event done");
|
2023-10-08 00:53:54 +00:00
|
|
|
}
|
2023-11-25 14:29:50 +00:00
|
|
|
} else if (e.kind == NostrKind.Encrypted_Custom_App_Data) {
|
2023-11-25 13:26:03 +00:00
|
|
|
console.log(e);
|
|
|
|
const err = await args.otherConfig.addEvent(e);
|
|
|
|
if (err instanceof Error) {
|
|
|
|
console.error(err);
|
|
|
|
}
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
2023-08-24 17:32:52 +00:00
|
|
|
|
2023-10-15 22:39:21 +00:00
|
|
|
// notification should be moved to after domain objects
|
|
|
|
{
|
|
|
|
const author = database.getProfilesByPublicKey(e.publicKey)
|
|
|
|
?.profile;
|
|
|
|
if (e.pubkey != ctx.publicKey.hex && e.parsedTags.p.includes(ctx.publicKey.hex)) {
|
|
|
|
notify(
|
|
|
|
author?.name ? author.name : "",
|
|
|
|
"new message",
|
|
|
|
author?.picture ? author.picture : "",
|
|
|
|
() => {
|
|
|
|
if (e.kind == NostrKind.DIRECT_MESSAGE) {
|
|
|
|
const k = PublicKey.FromHex(e.pubkey);
|
|
|
|
if (k instanceof Error) {
|
|
|
|
console.error(k);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
emit({
|
|
|
|
type: "SelectConversation",
|
|
|
|
pubkey: k,
|
|
|
|
});
|
|
|
|
} else if (e.kind == NostrKind.TEXT_NOTE) {
|
|
|
|
// todo
|
|
|
|
// open the default kind 1 app
|
|
|
|
} else {
|
|
|
|
// todo
|
|
|
|
// handle other types
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
|
|
|
yield model;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-11 18:09:38 +00:00
|
|
|
export async function handle_SendMessage(
|
|
|
|
event: SendMessage,
|
|
|
|
ctx: NostrAccountContext,
|
|
|
|
lamport: LamportTime,
|
2024-03-15 13:44:17 +00:00
|
|
|
db: Database_View,
|
|
|
|
args: {
|
|
|
|
navigationModel: NavigationModel;
|
|
|
|
social: Social_Model;
|
|
|
|
dm: {
|
|
|
|
currentConversation: PublicKey | undefined;
|
|
|
|
};
|
2024-03-15 15:46:11 +00:00
|
|
|
blowater_relay: SingleRelayConnection;
|
|
|
|
current_relay: SingleRelayConnection;
|
2024-03-15 13:44:17 +00:00
|
|
|
},
|
2023-09-11 18:09:38 +00:00
|
|
|
) {
|
2024-03-15 13:44:17 +00:00
|
|
|
if (event.text.length == 0) {
|
|
|
|
return new Error("can't send empty message");
|
|
|
|
}
|
2024-03-15 15:46:11 +00:00
|
|
|
|
|
|
|
let events: NostrEvent[];
|
2024-03-15 13:44:17 +00:00
|
|
|
if (args.navigationModel.activeNav == "DM") {
|
2024-03-15 15:46:11 +00:00
|
|
|
const events_send = await sendDirectMessages({
|
2023-10-07 23:23:53 +00:00
|
|
|
sender: ctx,
|
2024-03-15 13:44:17 +00:00
|
|
|
receiverPublicKey: args.dm.currentConversation as PublicKey,
|
2023-10-07 23:23:53 +00:00
|
|
|
message: event.text,
|
|
|
|
files: event.files,
|
|
|
|
lamport_timestamp: lamport.now(),
|
2024-03-15 15:46:11 +00:00
|
|
|
eventSender: args.blowater_relay,
|
2024-03-19 10:27:18 +00:00
|
|
|
tags: generateTags(event.text),
|
2023-10-07 23:23:53 +00:00
|
|
|
});
|
2024-03-15 15:46:11 +00:00
|
|
|
if (events_send instanceof Error) {
|
|
|
|
return events_send;
|
|
|
|
}
|
|
|
|
for (const event of events_send) {
|
|
|
|
const result = args.current_relay.sendEvent(event);
|
|
|
|
if (result instanceof Error) {
|
|
|
|
return result;
|
|
|
|
}
|
2023-10-07 23:23:53 +00:00
|
|
|
}
|
2024-03-15 15:46:11 +00:00
|
|
|
events = events_send;
|
2024-03-16 11:22:46 +00:00
|
|
|
} else if (args.navigationModel.activeNav == "Public") {
|
2024-03-15 13:44:17 +00:00
|
|
|
const nostr_event = await prepareNormalNostrEvent(ctx, {
|
|
|
|
content: event.text,
|
|
|
|
kind: NostrKind.TEXT_NOTE,
|
2024-03-19 10:27:18 +00:00
|
|
|
tags: generateTags(event.text),
|
2024-03-15 13:44:17 +00:00
|
|
|
});
|
2024-03-15 15:46:11 +00:00
|
|
|
const err = await args.current_relay.sendEvent(nostr_event);
|
2024-03-15 13:44:17 +00:00
|
|
|
if (err instanceof Error) {
|
|
|
|
return err;
|
2023-12-04 18:28:00 +00:00
|
|
|
}
|
2024-03-15 13:44:17 +00:00
|
|
|
events = [nostr_event];
|
|
|
|
} else {
|
|
|
|
return new Error(`${args.navigationModel.activeNav} should not send messages`);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const eventSent of events) {
|
|
|
|
const err = await db.addEvent(eventSent, undefined);
|
|
|
|
if (err instanceof Error) {
|
|
|
|
console.error(err);
|
2023-10-07 23:23:53 +00:00
|
|
|
}
|
2023-10-07 10:59:06 +00:00
|
|
|
}
|
2023-09-11 18:09:38 +00:00
|
|
|
}
|