small fixes for reaction popover and notifications

This commit is contained in:
hzrd149
2023-11-27 09:39:56 -06:00
parent 1804a04b82
commit d0fc4fb034
3 changed files with 50 additions and 45 deletions

View File

@@ -1,7 +1,7 @@
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { Subject } from "./subject"; import { Subject } from "./subject";
import { NostrEvent } from "../types/nostr-event"; import { NostrEvent } from "../types/nostr-event";
import { NostrOutgoingMessage, NostrRequestFilter } from "../types/nostr-query"; import { NostrOutgoingMessage, NostrOutgoingRequest, NostrRequestFilter } from "../types/nostr-query";
import Relay, { IncomingEvent } from "./relay"; import Relay, { IncomingEvent } from "./relay";
import relayPoolService from "../services/relay-pool"; import relayPoolService from "../services/relay-pool";
@@ -25,7 +25,7 @@ export default class NostrMultiSubscription {
this.name = name; this.name = name;
this.relayUrls = relayUrls; this.relayUrls = relayUrls;
this.relays = relayUrls.map((url) => relayPoolService.requestRelay(url)); this.relays = this.relayUrls.map((url) => relayPoolService.requestRelay(url));
} }
private handleEvent(event: IncomingEvent) { private handleEvent(event: IncomingEvent) {
if (this.state === NostrMultiSubscription.OPEN && event.subId === this.id && !this.seenEvents.has(event.body.id)) { if (this.state === NostrMultiSubscription.OPEN && event.subId === this.id && !this.seenEvents.has(event.body.id)) {
@@ -33,58 +33,70 @@ export default class NostrMultiSubscription {
this.seenEvents.add(event.body.id); this.seenEvents.add(event.body.id);
} }
} }
send(message: NostrOutgoingMessage) {
private relayQueries = new WeakMap<Relay, NostrRequestFilter>();
private updateRelayQueries() {
if (!this.query || this.state !== NostrMultiSubscription.OPEN) return;
const message: NostrOutgoingRequest = Array.isArray(this.query)
? ["REQ", this.id, ...this.query]
: ["REQ", this.id, this.query];
for (const relay of this.relays) {
if (this.relayQueries.get(relay) !== this.query) {
relay.send(message);
}
}
}
private handleRelayConnect(relay: Relay) {
this.updateRelayQueries();
}
private handleRelayDisconnect(relay: Relay) {
this.relayQueries.delete(relay);
}
sendToAll(message: NostrOutgoingMessage) {
for (const relay of this.relays) { for (const relay of this.relays) {
relay.send(message); relay.send(message);
} }
} }
/** listen for event and open events from relays */ /** listen for event and open events from relays */
private subscribeToRelays() { private connectToRelays() {
for (const relay of this.relays) { for (const relay of this.relays) {
relay.onEvent.subscribe(this.handleEvent, this); relay.onEvent.subscribe(this.handleEvent, this);
} relay.onOpen.subscribe(this.handleRelayConnect, this);
relay.onClose.subscribe(this.handleRelayDisconnect, this);
for (const url of this.relayUrls) { relayPoolService.addClaim(relay.url, this);
relayPoolService.addClaim(url, this);
} }
} }
/** listen for event and open events from relays */ /** stop listing to events from relays */
private unsubscribeFromRelays() { private disconnectFromRelays() {
for (const relay of this.relays) { for (const relay of this.relays) {
relay.onEvent.unsubscribe(this.handleEvent, this); relay.onEvent.unsubscribe(this.handleEvent, this);
} relay.onOpen.unsubscribe(this.handleRelayConnect, this);
relay.onClose.unsubscribe(this.handleRelayDisconnect, this);
for (const url of this.relayUrls) { relayPoolService.removeClaim(relay.url, this);
relayPoolService.removeClaim(url, this);
} }
} }
open() { open() {
if (!this.query) throw new Error("cant open without a query"); if (!this.query) throw new Error("Cant open without a query");
if (this.state === NostrMultiSubscription.OPEN) return this; if (this.state === NostrMultiSubscription.OPEN) return this;
this.state = NostrMultiSubscription.OPEN; this.state = NostrMultiSubscription.OPEN;
if (Array.isArray(this.query)) { this.connectToRelays();
this.send(["REQ", this.id, ...this.query]); this.updateRelayQueries();
} else this.send(["REQ", this.id, this.query]);
this.subscribeToRelays();
return this; return this;
} }
setQuery(query: NostrRequestFilter) { setQuery(query: NostrRequestFilter) {
this.query = query; this.query = query;
if (this.state === NostrMultiSubscription.OPEN) { this.updateRelayQueries();
if (Array.isArray(this.query)) {
this.send(["REQ", this.id, ...this.query]);
} else this.send(["REQ", this.id, this.query]);
}
return this; return this;
} }
setRelays(relays: string[]) { setRelays(relayUrls: string[]) {
this.unsubscribeFromRelays(); this.disconnectFromRelays();
const newRelays = relays.map((url) => relayPoolService.requestRelay(url)); const newRelays = relayUrls.map((url) => relayPoolService.requestRelay(url));
for (const relay of this.relays) { for (const relay of this.relays) {
if (!newRelays.includes(relay)) { if (!newRelays.includes(relay)) {
@@ -95,24 +107,14 @@ export default class NostrMultiSubscription {
} }
} }
} }
for (const relay of newRelays) {
if (!this.relays.includes(relay)) {
// if the subscription is open and it has a query
if (this.state === NostrMultiSubscription.OPEN && this.query) {
// open a connection to this relay
if (Array.isArray(this.query)) {
relay.send(["REQ", this.id, ...this.query]);
} else relay.send(["REQ", this.id, this.query]);
}
}
}
// set new relays // set new relays
this.relayUrls = relays; this.relayUrls = relayUrls;
this.relays = newRelays; this.relays = newRelays;
if (this.state === NostrMultiSubscription.OPEN) { if (this.state === NostrMultiSubscription.OPEN) {
this.subscribeToRelays(); this.connectToRelays();
this.updateRelayQueries();
} }
} }
close() { close() {
@@ -121,11 +123,11 @@ export default class NostrMultiSubscription {
// set state // set state
this.state = NostrMultiSubscription.CLOSED; this.state = NostrMultiSubscription.CLOSED;
// send close message // send close message
this.send(["CLOSE", this.id]); this.sendToAll(["CLOSE", this.id]);
// forget all seen events // forget all seen events
this.seenEvents.clear(); this.seenEvents.clear();
// unsubscribe from relay messages // unsubscribe from relay messages
this.unsubscribeFromRelays(); this.disconnectFromRelays();
return this; return this;
} }

View File

@@ -6,6 +6,7 @@ import {
PopoverBody, PopoverBody,
PopoverContent, PopoverContent,
PopoverTrigger, PopoverTrigger,
useBoolean,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import useEventReactions from "../../../hooks/use-event-reactions"; import useEventReactions from "../../../hooks/use-event-reactions";
@@ -22,6 +23,7 @@ import { getEventUID } from "../../../helpers/nostr/events";
export default function ReactionButton({ event, ...props }: { event: NostrEvent } & Omit<ButtonProps, "children">) { export default function ReactionButton({ event, ...props }: { event: NostrEvent } & Omit<ButtonProps, "children">) {
const { requestSignature } = useSigningContext(); const { requestSignature } = useSigningContext();
const reactions = useEventReactions(getEventUID(event)) ?? []; const reactions = useEventReactions(getEventUID(event)) ?? [];
const [popover, setPopover] = useBoolean();
const addReaction = async (emoji = "+", url?: string) => { const addReaction = async (emoji = "+", url?: string) => {
const draft = draftEventReaction(event, emoji, url); const draft = draftEventReaction(event, emoji, url);
@@ -31,11 +33,12 @@ export default function ReactionButton({ event, ...props }: { event: NostrEvent
const writeRelays = clientRelaysService.getWriteUrls(); const writeRelays = clientRelaysService.getWriteUrls();
new NostrPublishAction("Reaction", writeRelays, signed); new NostrPublishAction("Reaction", writeRelays, signed);
eventReactionsService.handleEvent(signed); eventReactionsService.handleEvent(signed);
setPopover.off();
} }
}; };
return ( return (
<Popover isLazy> <Popover isLazy isOpen={popover} onOpen={setPopover.on} onClose={setPopover.off}>
<PopoverTrigger> <PopoverTrigger>
<IconButton icon={<AddReactionIcon />} aria-label="Add Reaction" {...props}> <IconButton icon={<AddReactionIcon />} aria-label="Add Reaction" {...props}>
{reactions?.length ?? 0} {reactions?.length ?? 0}

View File

@@ -65,7 +65,7 @@ export default function ContentDiscoveryView() {
Back Back
</Button> </Button>
</Flex> </Flex>
<SimpleGrid columns={{ base: 1, md: 2, lg: 3, xl: 4 }}> <SimpleGrid columns={{ base: 1, md: 2, lg: 3, xl: 4 }} spacing="2">
{DMVs.map((appData) => ( {DMVs.map((appData) => (
<DVMCard key={appData.id} appData={appData} maxW="lg" /> <DVMCard key={appData.id} appData={appData} maxW="lg" />
))} ))}