mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-17 10:56:52 +02:00
* docs: add applesauce v5 upgrade plan Comprehensive migration plan covering: - Package updates (add applesauce-common, update to v5) - EventFactory import migration (applesauce-factory → applesauce-core) - Unified event loader setup - ActionHub → ActionRunner migration - useObservableMemo → use$ hook migration - New features: casting system, encrypted content caching - Documentation and skills updates needed * feat: upgrade applesauce libraries to v5 Major upgrade from applesauce v4 to v5 with breaking changes: Package updates: - applesauce-core: ^4.0.0 → ^5.0.0 - applesauce-actions: ^4.0.0 → ^5.0.0 - applesauce-loaders: ^4.0.0 → ^5.0.0 - applesauce-react: ^4.0.0 → ^5.0.0 - applesauce-relay: ^4.0.0 → ^5.0.0 - applesauce-signers: ^4.0.0 → ^5.0.0 - applesauce-accounts: ^4.0.0 → ^5.0.0 - Added new applesauce-common: ^5.0.0 package API migrations: - EventFactory: applesauce-factory → applesauce-core/event-factory - ActionHub → ActionRunner with async function pattern (not generators) - useObservableMemo → use$ hook across all components - Helper imports: article, highlight, threading, zap, comment, lists moved from applesauce-core to applesauce-common - parseCoordinate → parseReplaceableAddress - Subscription options: retries → reconnect - getEventPointerFromETag now returns null instead of throwing New features: - Unified event loader via createEventLoaderForStore - Updated loaders.ts to use v5 unified loader pattern Documentation: - Updated CLAUDE.md with v5 patterns and migration notes - Updated applesauce-core skill for v5 changes - Created new applesauce-common skill Test fixes: - Updated publish-spellbook.test.ts for v5 ActionRunner pattern - Updated publish-spell.test.ts with eventStore mock - Updated relay-selection.test.ts with valid test events - Updated loaders.test.ts with valid 64-char hex event IDs - Added createEventLoaderForStore mock --------- Co-authored-by: Claude <noreply@anthropic.com>
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
import { useEffect } from "react";
|
|
import { useEventStore, use$ } from "applesauce-react/hooks";
|
|
import accounts from "@/services/accounts";
|
|
import { useGrimoire } from "@/core/state";
|
|
import { addressLoader } from "@/services/loaders";
|
|
import type { RelayInfo } from "@/types/app";
|
|
import { normalizeRelayURL } from "@/lib/relay-url";
|
|
|
|
/**
|
|
* Hook that syncs active account with Grimoire state and fetches relay lists
|
|
*/
|
|
export function useAccountSync() {
|
|
const { setActiveAccount, setActiveAccountRelays } = useGrimoire();
|
|
const eventStore = useEventStore();
|
|
|
|
// Watch active account from accounts service
|
|
const activeAccount = use$(accounts.active$);
|
|
|
|
// Sync active account pubkey to state
|
|
useEffect(() => {
|
|
setActiveAccount(activeAccount?.pubkey);
|
|
}, [activeAccount?.pubkey, setActiveAccount]);
|
|
|
|
// Fetch and watch relay list (kind 10002) when account changes
|
|
useEffect(() => {
|
|
if (!activeAccount?.pubkey) {
|
|
return;
|
|
}
|
|
|
|
const pubkey = activeAccount.pubkey;
|
|
let lastRelayEventId: string | undefined;
|
|
|
|
// Subscribe to kind 10002 (relay list)
|
|
const subscription = addressLoader({
|
|
kind: 10002,
|
|
pubkey,
|
|
identifier: "",
|
|
}).subscribe();
|
|
|
|
// Watch for relay list event in store
|
|
const storeSubscription = eventStore
|
|
.replaceable(10002, pubkey, "")
|
|
.subscribe((relayListEvent) => {
|
|
if (!relayListEvent) return;
|
|
|
|
// Only process if this is a new event
|
|
if (relayListEvent.id === lastRelayEventId) return;
|
|
lastRelayEventId = relayListEvent.id;
|
|
|
|
// Parse relays from tags (NIP-65 format)
|
|
// Tag format: ["r", "relay-url", "read|write"]
|
|
// If no marker, relay is used for both read and write
|
|
const relays: RelayInfo[] = [];
|
|
const seenUrls = new Set<string>();
|
|
|
|
for (const tag of relayListEvent.tags) {
|
|
if (tag[0] === "r" && tag[1]) {
|
|
try {
|
|
const url = normalizeRelayURL(tag[1]);
|
|
if (seenUrls.has(url)) continue;
|
|
seenUrls.add(url);
|
|
|
|
const marker = tag[2];
|
|
relays.push({
|
|
url,
|
|
read: !marker || marker === "read",
|
|
write: !marker || marker === "write",
|
|
});
|
|
} catch (error) {
|
|
console.warn(
|
|
`Skipping invalid relay URL in Kind 10002 event: ${tag[1]}`,
|
|
error,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
setActiveAccountRelays(relays);
|
|
});
|
|
|
|
return () => {
|
|
subscription.unsubscribe();
|
|
storeSubscription.unsubscribe();
|
|
};
|
|
}, [activeAccount?.pubkey, eventStore, setActiveAccountRelays]);
|
|
}
|