mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-16 09:38:36 +02:00
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:
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"root":["./vite.config.ts"],"errors":true,"version":"5.9.3"}
|
||||
{"root":["./vite.config.ts"],"version":"5.6.3"}
|
||||
Reference in New Issue
Block a user