fix: Resolve TypeScript errors in gift wrap implementation

Fixed all TypeScript compilation errors:

**gift-wrap.ts:**
- Fix imports: remove non-existent mapEventsToStore, use pool.subscription
- Change Signer to ISigner from applesauce-signers
- Use firstValueFrom for Observable unwrapping
- Fix eventStore.event() - returns Observable, not direct event
- Fix GiftWrapsModel - returns array, not Set
- Proper subscription management with pool.subscription({ eventStore })

**InboxViewer.tsx:**
- Add missing imports: firstValueFrom, eventStore
- Fix getInboxes() usage - requires NostrEvent (kind 10002), not pubkey
- Fetch relay list event first, then extract inbox relays
- Add optional chaining for syncState (can be undefined)
- Handle pending events as array, not Set

**Build Status:**  Compiles successfully
This commit is contained in:
Claude
2026-01-14 20:14:26 +00:00
parent 01e5de882e
commit 38e39eafc5
3 changed files with 75 additions and 69 deletions

View File

@@ -7,8 +7,10 @@ import {
CheckCircle2,
Trash2,
} from "lucide-react";
import { firstValueFrom } from "rxjs";
import giftWrapManager from "@/services/gift-wrap";
import accountManager from "@/services/accounts";
import eventStore from "@/services/event-store";
import db, { DecryptedGiftWrap } from "@/services/db";
import { getInboxes } from "applesauce-core/helpers";
@@ -37,7 +39,7 @@ export default function InboxViewer({ action }: InboxViewerProps) {
.limit(50)
.toArray()
.then(setDecrypted);
}, [pubkey, page, syncState.decryptedCount]);
}, [pubkey, page, syncState?.decryptedCount]);
// Initial sync on mount
useEffect(() => {
@@ -46,11 +48,15 @@ export default function InboxViewer({ action }: InboxViewerProps) {
const syncGiftWraps = async () => {
setLoading(true);
try {
// Get inbox relays from user's relay list
const inboxRelays = Array.from(
await getInboxes(pubkey).then((set) => set || new Set()),
// Get inbox relays from user's kind 10002 relay list event
const relayListEvent = await firstValueFrom(
eventStore.replaceable({ kind: 10002, pubkey }),
);
const inboxRelays = relayListEvent
? Array.from(getInboxes(relayListEvent))
: [];
// Fallback to default relays if no inbox relays
const relays =
inboxRelays.length > 0
@@ -100,14 +106,15 @@ export default function InboxViewer({ action }: InboxViewerProps) {
setDecrypting(true);
try {
// Get pending gift wrap IDs
const pending = await new Promise<string[]>((resolve) => {
giftWrapManager.getPendingCount(pubkey).subscribe((set) => {
// Get IDs from EventStore
const ids = Array.from(set as any).map((e: any) => e.id);
resolve(ids);
});
});
// Get pending gift wrap events (returns array)
const pendingEvents = await firstValueFrom(
giftWrapManager.getPendingGiftWraps(pubkey),
);
// Extract IDs
const pending = Array.isArray(pendingEvents)
? pendingEvents.map((e) => e.id)
: [];
if (pending.length === 0) {
console.log("[InboxViewer] No pending gift wraps to decrypt");
@@ -190,20 +197,22 @@ export default function InboxViewer({ action }: InboxViewerProps) {
<div className="flex gap-4 flex-wrap">
<div className="flex items-center gap-2 px-3 py-2 bg-warning/10 text-warning rounded-lg">
<Package className="w-4 h-4" />
<span className="font-medium">{syncState.pendingCount}</span>
<span className="font-medium">{syncState?.pendingCount ?? 0}</span>
<span className="text-sm">Pending</span>
</div>
<div className="flex items-center gap-2 px-3 py-2 bg-success/10 text-success rounded-lg">
<CheckCircle2 className="w-4 h-4" />
<span className="font-medium">{syncState.decryptedCount}</span>
<span className="font-medium">
{syncState?.decryptedCount ?? 0}
</span>
<span className="text-sm">Decrypted</span>
</div>
{syncState.failedCount > 0 && (
{(syncState?.failedCount ?? 0) > 0 && (
<div className="flex items-center gap-2 px-3 py-2 bg-error/10 text-error rounded-lg">
<AlertCircle className="w-4 h-4" />
<span className="font-medium">{syncState.failedCount}</span>
<span className="font-medium">{syncState?.failedCount}</span>
<span className="text-sm">Failed</span>
</div>
)}
@@ -213,7 +222,7 @@ export default function InboxViewer({ action }: InboxViewerProps) {
<div className="flex gap-2 flex-wrap">
<button
onClick={handleDecryptAll}
disabled={decrypting || syncState.pendingCount === 0}
disabled={decrypting || (syncState?.pendingCount ?? 0) === 0}
className="btn btn-sm btn-primary"
>
{decrypting ? (
@@ -222,11 +231,11 @@ export default function InboxViewer({ action }: InboxViewerProps) {
Decrypting...
</>
) : (
<>Decrypt All Pending ({syncState.pendingCount})</>
<>Decrypt All Pending ({syncState?.pendingCount ?? 0})</>
)}
</button>
{syncState.failedCount > 0 && (
{(syncState?.failedCount ?? 0) > 0 && (
<button
onClick={() => giftWrapManager.clearErrors()}
className="btn btn-sm btn-ghost"
@@ -235,7 +244,7 @@ export default function InboxViewer({ action }: InboxViewerProps) {
</button>
)}
{syncState.decryptedCount > 0 && (
{(syncState?.decryptedCount ?? 0) > 0 && (
<button
onClick={handleClearAll}
className="btn btn-sm btn-ghost text-error"
@@ -254,13 +263,13 @@ export default function InboxViewer({ action }: InboxViewerProps) {
<div className="text-center space-y-4">
<Package className="w-12 h-12 mx-auto opacity-50" />
<p>No decrypted gift wraps yet.</p>
{syncState.pendingCount > 0 && (
{(syncState?.pendingCount ?? 0) > 0 && (
<button
onClick={handleDecryptAll}
disabled={decrypting}
className="btn btn-primary btn-sm"
>
Decrypt {syncState.pendingCount} Pending
Decrypt {syncState?.pendingCount} Pending
</button>
)}
</div>
@@ -317,9 +326,9 @@ export default function InboxViewer({ action }: InboxViewerProps) {
</div>
{/* Footer with sync info */}
{syncState.lastSyncAt > 0 && (
{(syncState?.lastSyncAt ?? 0) > 0 && (
<div className="p-2 text-xs text-center text-base-content/50 border-t border-base-300">
Last synced: {new Date(syncState.lastSyncAt).toLocaleString()}
Last synced: {new Date(syncState!.lastSyncAt).toLocaleString()}
</div>
)}
</div>

View File

@@ -1,14 +1,12 @@
import { BehaviorSubject, map, Subscription } from "rxjs";
import { BehaviorSubject, Subscription, firstValueFrom } from "rxjs";
import { createTimelineLoader } from "applesauce-loaders/loaders";
import { onlyEvents, mapEventsToStore } from "applesauce-core";
import { unlockGiftWrap, getGiftWrapSeal } from "applesauce-common/helpers";
import { GiftWrapsModel } from "applesauce-common/models";
import type { Signer } from "applesauce-signers";
import type { ISigner } from "applesauce-signers";
import type { NostrEvent } from "@/types/nostr";
import eventStore from "./event-store";
import pool from "./relay-pool";
import db from "./db";
import { getEventsForFilters } from "nostr-idb";
/**
* Gift wrap sync state
@@ -66,14 +64,13 @@ class GiftWrapManager {
);
try {
// Create timeline loader with cache fallback
// Create timeline loader
const timeline = createTimelineLoader(
pool,
relays,
{ kinds: [1059], "#p": [pubkey], limit: 100 },
{
eventStore,
cache: (filters) => getEventsForFilters(await db.open(), filters),
},
);
@@ -125,29 +122,32 @@ class GiftWrapManager {
console.log(`[GiftWrap] Subscribing to new gift wraps for ${pubkey}`);
// Subscribe to each relay
const subs = relays.map((relay) =>
pool
.relay(relay)
.subscription({
kinds: [1059],
"#p": [pubkey],
since: Math.floor(Date.now() / 1000),
})
.pipe(onlyEvents(), mapEventsToStore(eventStore))
.subscribe({
next: (event) => {
console.log("[GiftWrap] New gift wrap received:", event.id);
this.updateCounts(pubkey);
// Subscribe via pool.subscription (auto-adds to eventStore)
const sub = pool
.subscription(
relays,
[
{
kinds: [1059],
"#p": [pubkey],
since: Math.floor(Date.now() / 1000),
},
error: (err) => console.error("[GiftWrap] Subscription error:", err),
}),
);
],
{ eventStore },
)
.subscribe({
next: (response) => {
if (typeof response !== "string") {
// It's an event
console.log("[GiftWrap] New gift wrap received:", response.id);
this.updateCounts(pubkey);
}
},
error: (err) => console.error("[GiftWrap] Subscription error:", err),
});
// Store combined subscription
const combined = new Subscription();
subs.forEach((sub) => combined.add(sub));
this.subscriptions.set(key, combined);
// Store subscription
this.subscriptions.set(key, sub);
}
/**
@@ -163,12 +163,11 @@ class GiftWrapManager {
*/
async updateCounts(pubkey: string): Promise<void> {
// Get pending count from applesauce model
const pending = await new Promise<number>((resolve) => {
eventStore
.model(GiftWrapsModel, pubkey, true)
.pipe(map((set) => set.size))
.subscribe(resolve);
});
const pendingEvents = await firstValueFrom(
eventStore.model(GiftWrapsModel, pubkey, true),
);
// GiftWrapsModel returns an array of events
const pending = Array.isArray(pendingEvents) ? pendingEvents.length : 0;
// Get decrypted count from Dexie
const decrypted = await db.decryptedGiftWraps.count();
@@ -188,19 +187,17 @@ class GiftWrapManager {
}
/**
* Get observable of pending gift wrap count
* Get observable of pending gift wraps
*/
getPendingCount(pubkey: string) {
return eventStore
.model(GiftWrapsModel, pubkey, true)
.pipe(map((set) => set.size));
getPendingGiftWraps(pubkey: string) {
return eventStore.model(GiftWrapsModel, pubkey, true);
}
/**
* Decrypt a single gift wrap
* Returns cached result if already decrypted
*/
async decryptOne(giftWrapId: string, signer: Signer): Promise<NostrEvent> {
async decryptOne(giftWrapId: string, signer: ISigner): Promise<NostrEvent> {
// Check cache first
const cached = await db.decryptedGiftWraps.get(giftWrapId);
if (cached) {
@@ -216,8 +213,8 @@ class GiftWrapManager {
);
}
// Get gift wrap from EventStore
const gift = eventStore.event(giftWrapId);
// Get gift wrap from EventStore (returns Observable)
const gift = await firstValueFrom(eventStore.event(giftWrapId));
if (!gift) {
throw new Error(`Gift wrap not found: ${giftWrapId}`);
}
@@ -230,14 +227,14 @@ class GiftWrapManager {
await db.decryptedGiftWraps.add({
giftWrapId: gift.id,
rumorId: rumor.id,
rumor,
rumor: rumor as NostrEvent, // Rumor extends NostrEvent but without sig
sealPubkey: getGiftWrapSeal(gift)?.pubkey || "",
decryptedAt: Math.floor(Date.now() / 1000),
receivedAt: gift.created_at,
});
console.log("[GiftWrap] Decrypted successfully:", giftWrapId);
return rumor;
return rumor as NostrEvent;
} catch (err) {
const errorMessage = String(err);
console.error("[GiftWrap] Decryption failed:", giftWrapId, errorMessage);
@@ -259,7 +256,7 @@ class GiftWrapManager {
*/
async *decryptBatch(
giftWrapIds: string[],
signer: Signer,
signer: ISigner,
): AsyncGenerator<{
id: string;
status: "success" | "error";

View File

@@ -1 +1 @@
{"root":["./vite.config.ts"],"errors":true,"version":"5.9.3"}
{"root":["./vite.config.ts"],"version":"5.6.3"}