mirror of
https://github.com/BlowaterNostr/blowater.git
synced 2024-10-18 07:33:22 +00:00
parent
b8616ed8bf
commit
1cf773d27c
@ -27,6 +27,7 @@ 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 } from "../group-chat.ts";
|
||||
import { OtherConfig } from "./config-other.ts";
|
||||
|
||||
export async function Start(database: DexieDatabase) {
|
||||
console.log("Start the application");
|
||||
@ -92,6 +93,7 @@ export class App {
|
||||
public readonly conversationLists: ConversationLists;
|
||||
public readonly relayConfig: RelayConfig;
|
||||
public readonly groupChatController: GroupChatController;
|
||||
public readonly otherConfig: OtherConfig = new OtherConfig();
|
||||
|
||||
constructor(
|
||||
public readonly database: Database_Contextual_View,
|
||||
@ -155,6 +157,8 @@ export class App {
|
||||
}
|
||||
})();
|
||||
|
||||
this.otherConfig.syncFromRelay(this.pool, this.ctx);
|
||||
|
||||
// create group synchronization
|
||||
(async () => {
|
||||
const stream = await this.pool.newSub("group creations", {
|
||||
@ -382,6 +386,7 @@ export function AppComponent(props: {
|
||||
allUserInfo: app.conversationLists,
|
||||
profilesSyncer: app.profileSyncer,
|
||||
eventSyncer: app.eventSyncer,
|
||||
pinListGetter: app.otherConfig,
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
@ -31,10 +31,10 @@ import {
|
||||
DirectedMessage_Event,
|
||||
Encrypted_Event,
|
||||
getTags,
|
||||
PinContact,
|
||||
PinConversation,
|
||||
Profile_Nostr_Event,
|
||||
Text_Note_Event,
|
||||
UnpinContact,
|
||||
UnpinConversation,
|
||||
} from "../nostr.ts";
|
||||
import { MessageThread } from "./dm.tsx";
|
||||
import { DexieDatabase } from "./dexie-db.ts";
|
||||
@ -58,8 +58,8 @@ export type UI_Interaction_Event =
|
||||
| DirectMessagePanelUpdate
|
||||
| BackToContactList
|
||||
| MyProfileUpdate
|
||||
| PinContact
|
||||
| UnpinContact
|
||||
| PinConversation
|
||||
| UnpinConversation
|
||||
| SignInEvent
|
||||
| RelayConfigChange
|
||||
| CreateGroupChat
|
||||
@ -194,8 +194,20 @@ export async function* UI_Interaction_Update(args: {
|
||||
model.dm.currentSelectedContact = undefined;
|
||||
} else if (event.type == "SelectConversationType") {
|
||||
model.dm.selectedContactGroup = event.group;
|
||||
} else if (event.type == "PinContact" || event.type == "UnpinContact") {
|
||||
console.log("todo: handle", event.type);
|
||||
} else if (event.type == "PinConversation") {
|
||||
app.otherConfig.addPin(event.pubkey);
|
||||
const err = app.otherConfig.saveToRelay(pool, app.ctx);
|
||||
if (err instanceof Error) {
|
||||
console.error(err);
|
||||
continue;
|
||||
}
|
||||
} else if (event.type == "UnpinConversation") {
|
||||
app.otherConfig.removePin(event.pubkey);
|
||||
const err = app.otherConfig.saveToRelay(pool, app.ctx);
|
||||
if (err instanceof Error) {
|
||||
console.error(err);
|
||||
continue;
|
||||
}
|
||||
} //
|
||||
//
|
||||
// Editor
|
||||
|
@ -7,7 +7,7 @@ import { PrivateKey } from "../lib/nostr-ts/key.ts";
|
||||
Deno.test("Other Configs", async () => {
|
||||
{
|
||||
const config = OtherConfig.Empty();
|
||||
assertEquals(config.pinList, new Set());
|
||||
assertEquals(config.getPinList(), new Set());
|
||||
}
|
||||
|
||||
const ctx = InMemoryAccountContext.Generate();
|
||||
@ -25,7 +25,7 @@ Deno.test("Other Configs", async () => {
|
||||
});
|
||||
const config = await OtherConfig.FromNostrEvent(event, ctx);
|
||||
if (config instanceof Error) fail(config.message);
|
||||
assertEquals(config.pinList, new Set([pub.bech32(), pub2.bech32()]));
|
||||
assertEquals(config.getPinList(), new Set([pub.hex, pub2.hex]));
|
||||
|
||||
// encode back to events
|
||||
const event_2 = await config.toNostrEvent(ctx);
|
||||
@ -33,6 +33,6 @@ Deno.test("Other Configs", async () => {
|
||||
|
||||
const config_2 = await OtherConfig.FromNostrEvent(event_2, ctx);
|
||||
if (config_2 instanceof Error) fail(config_2.message);
|
||||
assertEquals(config.pinList, config_2.pinList);
|
||||
assertEquals(config.getPinList(), config_2.getPinList());
|
||||
}
|
||||
});
|
||||
|
@ -1,13 +1,26 @@
|
||||
import { prepareParameterizedEvent } from "../lib/nostr-ts/event.ts";
|
||||
import { PublicKey } from "../lib/nostr-ts/key.ts";
|
||||
import { NostrAccountContext, NostrEvent, NostrKind } from "../lib/nostr-ts/nostr.ts";
|
||||
import { ConnectionPool } from "../lib/nostr-ts/relay.ts";
|
||||
import { PinListGetter } from "./conversation-list.tsx";
|
||||
|
||||
export class OtherConfig {
|
||||
export class OtherConfig implements PinListGetter {
|
||||
static Empty() {
|
||||
return new OtherConfig();
|
||||
}
|
||||
|
||||
readonly pinList = new Set<string>(); // set of pubkeys in npub format
|
||||
private pinList = new Set<string>(); // set of pubkeys in npub format
|
||||
getPinList(): Set<string> {
|
||||
return this.pinList;
|
||||
}
|
||||
|
||||
addPin(pubkey: string) {
|
||||
this.pinList.add(pubkey);
|
||||
}
|
||||
|
||||
removePin(pubkey: string) {
|
||||
this.pinList.delete(pubkey);
|
||||
}
|
||||
|
||||
static async FromNostrEvent(event: NostrEvent<NostrKind.Custom_App_Data>, ctx: NostrAccountContext) {
|
||||
const decrypted = await ctx.decrypt(ctx.publicKey.hex, event.content);
|
||||
@ -17,11 +30,11 @@ export class OtherConfig {
|
||||
const pinList = JSON.parse(decrypted);
|
||||
const c = new OtherConfig();
|
||||
for (const pin of pinList) {
|
||||
const pubkey = PublicKey.FromBech32(pin);
|
||||
const pubkey = PublicKey.FromString(pin);
|
||||
if (pubkey instanceof Error) {
|
||||
continue;
|
||||
}
|
||||
c.pinList.add(pubkey.bech32());
|
||||
c.pinList.add(pubkey.hex);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@ -38,8 +51,46 @@ export class OtherConfig {
|
||||
content: encryptedContent,
|
||||
d: OtherConfig.name,
|
||||
kind: NostrKind.Custom_App_Data,
|
||||
created_at: Date.now() / 1000,
|
||||
});
|
||||
return event;
|
||||
}
|
||||
|
||||
async saveToRelay(pool: ConnectionPool, ctx: NostrAccountContext) {
|
||||
const nostrEvent = await this.toNostrEvent(ctx);
|
||||
if (nostrEvent instanceof Error) {
|
||||
return nostrEvent;
|
||||
}
|
||||
const err = pool.sendEvent(nostrEvent);
|
||||
if (err instanceof Error) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
async syncFromRelay(pool: ConnectionPool, ctx: NostrAccountContext) {
|
||||
const stream = await pool.newSub(OtherConfig.name, {
|
||||
"#d": [OtherConfig.name],
|
||||
authors: [ctx.publicKey.hex],
|
||||
kinds: [NostrKind.Custom_App_Data],
|
||||
});
|
||||
if (stream instanceof Error) {
|
||||
throw stream; // impossible
|
||||
}
|
||||
for await (const msg of stream.chan) {
|
||||
if (msg.res.type == "EOSE") {
|
||||
continue;
|
||||
}
|
||||
console.log("pin list", msg);
|
||||
const config = await OtherConfig.FromNostrEvent(
|
||||
// @ts-ignore
|
||||
msg.res.event,
|
||||
ctx,
|
||||
);
|
||||
if (config instanceof Error) {
|
||||
console.error(config);
|
||||
continue;
|
||||
}
|
||||
this.pinList = config.pinList;
|
||||
console.log(this.pinList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,6 @@ export interface ConversationSummary {
|
||||
profile: Profile_Nostr_Event | undefined;
|
||||
newestEventSendByMe: NostrEvent | undefined;
|
||||
newestEventReceivedByMe: NostrEvent | undefined;
|
||||
pinEvent: {
|
||||
readonly created_at: number;
|
||||
readonly content: CustomAppData;
|
||||
} | undefined;
|
||||
}
|
||||
|
||||
export function getConversationSummaryFromPublicKey(k: PublicKey, users: Map<string, ConversationSummary>) {
|
||||
@ -78,7 +74,6 @@ export class ConversationLists implements ConversationListRetriever {
|
||||
}
|
||||
} else {
|
||||
const newUserInfo: ConversationSummary = {
|
||||
pinEvent: undefined,
|
||||
pubkey: PublicKey.FromHex(event.pubkey) as PublicKey,
|
||||
newestEventReceivedByMe: undefined,
|
||||
newestEventSendByMe: undefined,
|
||||
@ -139,7 +134,6 @@ export class ConversationLists implements ConversationListRetriever {
|
||||
} else {
|
||||
const newUserInfo: ConversationSummary = {
|
||||
pubkey: PublicKey.FromHex(whoAm_I_TalkingTo) as PublicKey,
|
||||
pinEvent: undefined,
|
||||
newestEventReceivedByMe: undefined,
|
||||
newestEventSendByMe: undefined,
|
||||
profile: undefined,
|
||||
|
@ -8,11 +8,12 @@ import { emitFunc } from "../event-bus.ts";
|
||||
import { PinIcon, UnpinIcon } from "./icons/mod.tsx";
|
||||
import { SearchUpdate } from "./search_model.ts";
|
||||
import { PublicKey } from "../lib/nostr-ts/key.ts";
|
||||
import { PinContact, UnpinContact } from "../nostr.ts";
|
||||
import { PinConversation, UnpinConversation } from "../nostr.ts";
|
||||
import { PrimaryTextColor } from "./style/colors.ts";
|
||||
import { ButtonGroup } from "./components/button-group.tsx";
|
||||
import { ChatIcon } from "./icons2/chat-icon.tsx";
|
||||
import { StartCreateGroupChat } from "./create-group.tsx";
|
||||
import { OtherConfig } from "./config-other.ts";
|
||||
|
||||
export interface ConversationListRetriever {
|
||||
getContacts: () => Iterable<ConversationSummary>;
|
||||
@ -25,8 +26,8 @@ export type ConversationType = "Contacts" | "Strangers" | "Group";
|
||||
export type ContactUpdate =
|
||||
| SelectConversationType
|
||||
| SearchUpdate
|
||||
| PinContact
|
||||
| UnpinContact
|
||||
| PinConversation
|
||||
| UnpinConversation
|
||||
| StartCreateGroupChat;
|
||||
|
||||
export type SelectConversationType = {
|
||||
@ -39,6 +40,7 @@ type Props = {
|
||||
convoListRetriever: ConversationListRetriever;
|
||||
currentSelected: PublicKey | undefined;
|
||||
selectedContactGroup: ConversationType;
|
||||
pinListGetter: PinListGetter;
|
||||
hasNewMessages: Set<string>;
|
||||
};
|
||||
export function ConversationList(props: Props) {
|
||||
@ -153,15 +155,21 @@ export function ConversationList(props: Props) {
|
||||
<ContactGroup
|
||||
contacts={Array.from(convoListToRender.values())}
|
||||
currentSelected={props.currentSelected}
|
||||
pinListGetter={props.pinListGetter}
|
||||
emit={props.emit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export interface PinListGetter {
|
||||
getPinList(): Set<string>;
|
||||
}
|
||||
|
||||
type ConversationListProps = {
|
||||
contacts: { userInfo: ConversationSummary; isMarked: boolean }[];
|
||||
currentSelected: PublicKey | undefined;
|
||||
pinListGetter: PinListGetter;
|
||||
emit: emitFunc<ContactUpdate>;
|
||||
};
|
||||
|
||||
@ -170,10 +178,11 @@ function ContactGroup(props: ConversationListProps) {
|
||||
props.contacts.sort((a, b) => {
|
||||
return sortUserInfo(a.userInfo, b.userInfo);
|
||||
});
|
||||
const pinList = props.pinListGetter.getPinList();
|
||||
const pinned = [];
|
||||
const unpinned = [];
|
||||
for (const contact of props.contacts) {
|
||||
if (contact.userInfo.pinEvent && contact.userInfo.pinEvent.content.type == "PinContact") {
|
||||
if (pinList.has(contact.userInfo.pubkey.hex)) {
|
||||
pinned.push(contact);
|
||||
} else {
|
||||
unpinned.push(contact);
|
||||
@ -201,6 +210,7 @@ function ContactGroup(props: ConversationListProps) {
|
||||
<ConversationListItem
|
||||
userInfo={contact.userInfo}
|
||||
isMarked={contact.isMarked}
|
||||
isPinned={true}
|
||||
/>
|
||||
|
||||
<button
|
||||
@ -211,7 +221,7 @@ function ContactGroup(props: ConversationListProps) {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
props.emit({
|
||||
type: "UnpinContact",
|
||||
type: "UnpinConversation",
|
||||
pubkey: contact.userInfo.pubkey.hex,
|
||||
});
|
||||
}}
|
||||
@ -246,6 +256,7 @@ function ContactGroup(props: ConversationListProps) {
|
||||
<ConversationListItem
|
||||
userInfo={contact.userInfo}
|
||||
isMarked={contact.isMarked}
|
||||
isPinned={false}
|
||||
/>
|
||||
|
||||
<button
|
||||
@ -256,7 +267,7 @@ function ContactGroup(props: ConversationListProps) {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
props.emit({
|
||||
type: "PinContact",
|
||||
type: "PinConversation",
|
||||
pubkey: contact.userInfo.pubkey.hex,
|
||||
});
|
||||
}}
|
||||
@ -280,6 +291,7 @@ function ContactGroup(props: ConversationListProps) {
|
||||
type ListItemProps = {
|
||||
userInfo: ConversationSummary;
|
||||
isMarked: boolean;
|
||||
isPinned: boolean;
|
||||
};
|
||||
|
||||
function ConversationListItem(props: ListItemProps) {
|
||||
@ -316,7 +328,7 @@ function ConversationListItem(props: ListItemProps) {
|
||||
</span>
|
||||
)
|
||||
: undefined}
|
||||
{props.userInfo.pinEvent != undefined && props.userInfo.pinEvent.content.type == "PinContact"
|
||||
{props.isPinned
|
||||
? (
|
||||
<PinIcon
|
||||
class={tw`w-3 h-3 absolute top-0 right-0`}
|
||||
|
@ -28,6 +28,7 @@ type DirectMessageContainerProps = {
|
||||
allUserInfo: ConversationLists;
|
||||
profilesSyncer: ProfileSyncer;
|
||||
eventSyncer: EventSyncer;
|
||||
pinListGetter: cl.PinListGetter;
|
||||
} & DM_Model;
|
||||
|
||||
export type MessageThread = {
|
||||
|
@ -15,10 +15,10 @@ import { NostrEvent, NostrKind } from "../lib/nostr-ts/nostr.ts";
|
||||
import {
|
||||
DirectedMessage_Event,
|
||||
Parsed_Event,
|
||||
PinContact,
|
||||
PinConversation,
|
||||
Profile_Nostr_Event,
|
||||
Text_Note_Event,
|
||||
UnpinContact,
|
||||
UnpinConversation,
|
||||
} from "../nostr.ts";
|
||||
import { ProfileData, ProfileSyncer } from "../features/profile.ts";
|
||||
import { MessageThread } from "./dm.tsx";
|
||||
@ -83,7 +83,7 @@ interface DirectMessagePanelProps {
|
||||
|
||||
db: Database_Contextual_View;
|
||||
emit: emitFunc<
|
||||
EditorEvent | DirectMessagePanelUpdate | PinContact | UnpinContact
|
||||
EditorEvent | DirectMessagePanelUpdate | PinConversation | UnpinConversation
|
||||
>;
|
||||
profilesSyncer: ProfileSyncer;
|
||||
eventSyncer: EventSyncer;
|
||||
|
@ -102,7 +102,6 @@ export class RelayConfig {
|
||||
this.config = Automerge.change(this.config, "add", (config) => {
|
||||
config[url] = true;
|
||||
});
|
||||
const hex = secp256k1.utils.bytesToHex(this.save());
|
||||
}
|
||||
|
||||
async remove(url: string) {
|
||||
|
@ -43,7 +43,6 @@ export class GroupChatController {
|
||||
this.conversationLists.groupChatSummaries.set(args.groupKey.bech32, {
|
||||
newestEventReceivedByMe: undefined,
|
||||
newestEventSendByMe: undefined,
|
||||
pinEvent: undefined,
|
||||
profile: undefined,
|
||||
pubkey: args.groupKey.toPublicKey(),
|
||||
});
|
||||
|
10
nostr.ts
10
nostr.ts
@ -55,15 +55,15 @@ export type DirectedMessage_Event = Parsed_Event<NostrKind.DIRECT_MESSAGE> & {
|
||||
};
|
||||
export type Encrypted_Event = DirectedMessage_Event;
|
||||
|
||||
export type CustomAppData = PinContact | UnpinContact | UserLogin;
|
||||
export type CustomAppData = PinConversation | UnpinConversation | UserLogin;
|
||||
|
||||
export type PinContact = {
|
||||
type: "PinContact";
|
||||
export type PinConversation = {
|
||||
type: "PinConversation";
|
||||
pubkey: string;
|
||||
};
|
||||
|
||||
export type UnpinContact = {
|
||||
type: "UnpinContact";
|
||||
export type UnpinConversation = {
|
||||
type: "UnpinConversation";
|
||||
pubkey: string;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user