fix: correct API usage for EventFactory and NostrEvent types

## ComposeDialog
- Fix useProfile import (from @/hooks/useProfile, not applesauce-react)
- Use hub.factory.build() and hub.factory.sign() instead of hub.run()
- Remove unused EventFactory type import

## RelaySelector
- Simplify to not require relay pool stats (not available in API)
- Remove connection status indicators (Wifi/WifiOff icons)
- Show selected relays only, with add/remove functionality
- Removed unused RelayItem component

## thread-builder
- Import getSeenRelays helper for relay hints
- Replace event.relay (doesn't exist on NostrEvent) with getSeenRelays()
- Use first seen relay as relay hint in tags
- Remove references.mentions usage (property doesn't exist)

All API calls now match actual types and available methods.
This commit is contained in:
Claude
2026-01-12 16:23:46 +00:00
parent 05009bcbfb
commit 26ba678d91
3 changed files with 50 additions and 153 deletions

View File

@@ -24,10 +24,9 @@ import type { NostrEvent } from "nostr-tools/core";
import { relayListCache } from "@/services/relay-list-cache";
import { Send, Eye, Edit3, AtSign, X } from "lucide-react";
import { getDisplayName } from "@/lib/nostr-utils";
import { useProfile } from "applesauce-react/hooks";
import { useProfile } from "@/hooks/useProfile";
import { RelaySelector } from "@/components/RelaySelector";
import { PowerTools } from "@/components/PowerTools";
import type { EventFactory } from "applesauce-core/event-factory";
export interface ComposeDialogProps {
/** Whether dialog is open */
@@ -140,13 +139,12 @@ export function ComposeDialog({
}
// Create and sign event
const event = await hub.run(
async ({ factory }: { factory: EventFactory }) => {
const unsigned = factory.event(kind, messageContent, tags);
const signed = await factory.sign(unsigned);
return signed;
},
);
const draft = await hub.factory.build({
kind,
content: messageContent,
tags,
});
const event = await hub.factory.sign(draft);
// Publish to selected relays
await publishEventToRelays(event, selectedRelays);

View File

@@ -7,10 +7,8 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { X, Plus, Wifi, WifiOff, Settings2 } from "lucide-react";
import { X, Plus, Settings2 } from "lucide-react";
import { normalizeURL } from "applesauce-core/helpers";
import pool from "@/services/relay-pool";
import { use$ } from "applesauce-react/hooks";
export interface RelaySelectorProps {
/** Currently selected relays */
@@ -38,9 +36,6 @@ export function RelaySelector({
const [newRelayUrl, setNewRelayUrl] = useState("");
const [isOpen, setIsOpen] = useState(false);
// Get relay pool stats
const relayStats = use$(pool.stats$) || new Map();
// Handle add relay
const handleAddRelay = useCallback(() => {
if (!newRelayUrl.trim()) return;
@@ -89,23 +84,6 @@ export function RelaySelector({
[selectedRelays, handleRemoveRelay, onRelaysChange, maxRelays],
);
// Get relay connection status
const getRelayStatus = useCallback(
(relay: string): "connected" | "connecting" | "disconnected" => {
const stats = relayStats.get(relay);
if (!stats) return "disconnected";
// Check if there are any active subscriptions
if (stats.connectionState === "open") return "connected";
if (stats.connectionState === "connecting") return "connecting";
return "disconnected";
},
[relayStats],
);
// Get all known relays from pool
const knownRelays: string[] = Array.from(relayStats.keys()) as string[];
return (
<Popover open={isOpen} onOpenChange={setIsOpen}>
<PopoverTrigger asChild>
@@ -125,54 +103,33 @@ export function RelaySelector({
</div>
{/* Selected Relays */}
{selectedRelays.length > 0 && (
<div className="p-4 border-b bg-muted/30">
<div className="text-xs font-medium mb-2 text-muted-foreground">
SELECTED ({selectedRelays.length})
</div>
<ScrollArea className="flex-1 p-4">
{selectedRelays.length > 0 ? (
<div className="space-y-2">
{selectedRelays.map((relay: string) => (
<RelayItem
<div
key={relay}
relay={relay}
status={getRelayStatus(relay)}
selected={true}
onToggle={() => {
handleRemoveRelay(relay);
}}
/>
className="flex items-center gap-2 p-2 rounded-md bg-muted/50"
>
<div className="flex-1 min-w-0">
<div className="text-sm font-mono truncate">{relay}</div>
</div>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 flex-shrink-0 hover:bg-destructive/10 hover:text-destructive"
onClick={() => handleRemoveRelay(relay)}
>
<X className="h-4 w-4" />
</Button>
</div>
))}
</div>
</div>
)}
{/* Available Relays */}
<ScrollArea className="flex-1 p-4">
<div className="space-y-2">
<div className="text-xs font-medium mb-2 text-muted-foreground">
AVAILABLE
) : (
<div className="text-sm text-muted-foreground italic py-4 text-center">
No relays selected. Add relays below.
</div>
{knownRelays
.filter((relay: string) => !selectedRelays.includes(relay))
.map((relay: string) => (
<RelayItem
key={relay}
relay={relay}
status={getRelayStatus(relay)}
selected={false}
onToggle={() => {
handleToggleRelay(relay);
}}
/>
))}
{knownRelays.filter((r: string) => !selectedRelays.includes(r))
.length === 0 && (
<div className="text-sm text-muted-foreground italic py-4 text-center">
No other relays available
</div>
)}
</div>
)}
</ScrollArea>
{/* Add Relay */}
@@ -205,60 +162,3 @@ export function RelaySelector({
</Popover>
);
}
/**
* Individual relay item
*/
function RelayItem({
relay,
status,
selected,
onToggle,
}: {
relay: string;
status: "connected" | "connecting" | "disconnected";
selected: boolean;
onToggle: () => void;
}) {
return (
<div
className="flex items-center gap-2 p-2 rounded-md hover:bg-muted/50 transition-colors cursor-pointer"
onClick={onToggle}
>
{/* Status Indicator */}
{status === "connected" && (
<Wifi className="w-4 h-4 text-green-500 flex-shrink-0" />
)}
{status === "connecting" && (
<Wifi className="w-4 h-4 text-yellow-500 flex-shrink-0 animate-pulse" />
)}
{status === "disconnected" && (
<WifiOff className="w-4 h-4 text-muted-foreground flex-shrink-0" />
)}
{/* Relay URL */}
<div className="flex-1 min-w-0">
<div className="text-sm font-mono truncate">{relay}</div>
</div>
{/* Selection Indicator */}
{selected ? (
<Button
variant="ghost"
size="icon"
className="h-6 w-6 flex-shrink-0 hover:bg-destructive/10 hover:text-destructive"
>
<X className="h-4 w-4" />
</Button>
) : (
<Button
variant="ghost"
size="icon"
className="h-6 w-6 flex-shrink-0 hover:bg-primary/10"
>
<Plus className="h-4 w-4" />
</Button>
)}
</div>
);
}

View File

@@ -1,5 +1,6 @@
import type { NostrEvent } from "nostr-tools/core";
import { getNip10References } from "applesauce-common/helpers/threading";
import { getSeenRelays } from "applesauce-core/helpers/relays";
/**
* Thread tags for an event reply
@@ -29,28 +30,34 @@ export function buildNip10Tags(
const tags: string[][] = [];
const references = getNip10References(replyTo);
// Get relay hint from where event was seen
const seenRelaysSet = getSeenRelays(replyTo);
const relayHint = seenRelaysSet ? Array.from(seenRelaysSet)[0] : undefined;
// Add root tag
if (references.root) {
const root = references.root.e || references.root.a;
if (root && "id" in root) {
// EventPointer
const relay = root.relays?.[0];
const relay = root.relays?.[0] || relayHint;
tags.push(
relay ? ["e", root.id, relay, "root"] : ["e", root.id, "", "root"],
);
}
} else {
// This is the root - mark it as such
const relay = replyTo.relay;
tags.push(
relay ? ["e", replyTo.id, relay, "root"] : ["e", replyTo.id, "", "root"],
relayHint
? ["e", replyTo.id, relayHint, "root"]
: ["e", replyTo.id, "", "root"],
);
}
// Add reply tag (always the event we're directly replying to)
const relay = replyTo.relay;
tags.push(
relay ? ["e", replyTo.id, relay, "reply"] : ["e", replyTo.id, "", "reply"],
relayHint
? ["e", replyTo.id, relayHint, "reply"]
: ["e", replyTo.id, "", "reply"],
);
// Collect all mentioned pubkeys
@@ -59,16 +66,6 @@ export function buildNip10Tags(
// Add author of reply-to event
mentionedPubkeys.add(replyTo.pubkey);
// Add authors from thread history
if (references.mentions) {
for (const mention of references.mentions) {
const pointer = mention.e || mention.a;
if (pointer && "pubkey" in pointer && pointer.pubkey) {
mentionedPubkeys.add(pointer.pubkey);
}
}
}
// Add additional mentions
for (const pubkey of additionalMentions) {
mentionedPubkeys.add(pubkey);
@@ -81,7 +78,7 @@ export function buildNip10Tags(
return {
tags,
relayHint: relay,
relayHint,
};
}
@@ -104,6 +101,10 @@ export function buildNip22Tags(
): ThreadTags {
const tags: string[][] = [];
// Get relay hint from where event was seen
const seenRelaysSet = getSeenRelays(replyTo);
const relayHint = seenRelaysSet ? Array.from(seenRelaysSet)[0] : undefined;
// Add K tag (kind of parent event)
tags.push(["K", String(replyTo.kind)]);
@@ -114,14 +115,12 @@ export function buildNip22Tags(
// Use A tag for parameterized replaceable events
const dTag = replyTo.tags.find((t: string[]) => t[0] === "d")?.[1] || "";
const coordinate = `${replyTo.kind}:${replyTo.pubkey}:${dTag}`;
const relay = replyTo.relay;
tags.push(relay ? ["A", coordinate, relay] : ["A", coordinate]);
tags.push(relayHint ? ["A", coordinate, relayHint] : ["A", coordinate]);
} else {
// Use E tag for regular and replaceable events
const relay = replyTo.relay;
tags.push(
relay
? ["E", replyTo.id, relay, replyTo.pubkey]
relayHint
? ["E", replyTo.id, relayHint, replyTo.pubkey]
: ["E", replyTo.id, "", replyTo.pubkey],
);
}
@@ -145,7 +144,7 @@ export function buildNip22Tags(
return {
tags,
relayHint: replyTo.relay,
relayHint,
};
}