diff --git a/src/components/Home.tsx b/src/components/Home.tsx index 8226e47..9856543 100644 --- a/src/components/Home.tsx +++ b/src/components/Home.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, Activity } from "react"; +import { useState, useEffect } from "react"; import { useGrimoire } from "@/core/state"; import { useAccountSync } from "@/hooks/useAccountSync"; import Feed from "./nostr/Feed"; @@ -141,21 +141,16 @@ export default function Home() {
- {Object.values(state.workspaces).map((workspace) => ( - - {workspace.layout === null ? ( + {state.workspaces[state.activeWorkspaceId] && ( + <> + {state.workspaces[state.activeWorkspaceId].layout === null ? ( setCommandLauncherOpen(true)} /> ) : ( { // When Mosaic removes a node from the layout, clean up the window @@ -166,8 +161,8 @@ export default function Home() { className="mosaic-blueprint-theme" /> )} - - ))} + + )}
diff --git a/src/components/nostr/RichText/Hashtag.tsx b/src/components/nostr/RichText/Hashtag.tsx index 9580772..a95e679 100644 --- a/src/components/nostr/RichText/Hashtag.tsx +++ b/src/components/nostr/RichText/Hashtag.tsx @@ -5,13 +5,5 @@ interface HashtagNodeProps { } export function Hashtag({ node }: HashtagNodeProps) { - return ( - e.preventDefault()} - > - #{node.hashtag} - - ); + return #{node.hashtag}; } diff --git a/src/components/nostr/RichText/Text.tsx b/src/components/nostr/RichText/Text.tsx index 13e9801..286ed85 100644 --- a/src/components/nostr/RichText/Text.tsx +++ b/src/components/nostr/RichText/Text.tsx @@ -12,23 +12,24 @@ function hasRTLCharacters(text: string): boolean { export function Text({ node }: TextNodeProps) { const text = node.value; - + // If no newlines, render as inline span if (!text.includes("\n")) { const isRTL = hasRTLCharacters(text); return {text || "\u00A0"}; } - - // If has newlines, use divs for proper RTL per line + + // If has newlines, use spans with
tags const lines = text.split("\n"); return ( <> {lines.map((line, idx) => { const isRTL = hasRTLCharacters(line); return ( -
- {line || "\u00A0"} -
+ + {line || "\u00A0"} + {idx < lines.length - 1 &&
} +
); })} diff --git a/src/components/nostr/kinds/BaseEventRenderer.tsx b/src/components/nostr/kinds/BaseEventRenderer.tsx index a68e63a..1eeabf7 100644 --- a/src/components/nostr/kinds/BaseEventRenderer.tsx +++ b/src/components/nostr/kinds/BaseEventRenderer.tsx @@ -30,7 +30,7 @@ export function EventAuthor({ pubkey }: { pubkey: string }) {
); diff --git a/src/hooks/useAccountSync.ts b/src/hooks/useAccountSync.ts index cc2843e..551bf9c 100644 --- a/src/hooks/useAccountSync.ts +++ b/src/hooks/useAccountSync.ts @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { useObservableMemo, useEventStore } from "applesauce-react/hooks"; +import { useEventStore, useObservableMemo } from "applesauce-react/hooks"; import accounts from "@/services/accounts"; import { useGrimoire } from "@/core/state"; import { getInboxes, getOutboxes } from "applesauce-core/helpers"; diff --git a/src/hooks/useProfile.ts b/src/hooks/useProfile.ts index f382a61..f83785d 100644 --- a/src/hooks/useProfile.ts +++ b/src/hooks/useProfile.ts @@ -1,32 +1,54 @@ -import { useEffect } from "react"; -import { kinds } from "nostr-tools"; +import { useState, useEffect } from "react"; import { profileLoader } from "@/services/loaders"; -import { useEventStore, useObservableMemo } from "applesauce-react/hooks"; import { ProfileContent } from "applesauce-core/helpers"; -import { ProfileModel } from "applesauce-core/models/profile"; +import { kinds } from "nostr-tools"; +import db from "@/services/db"; export function useProfile(pubkey: string): ProfileContent | undefined { - const eventStore = useEventStore(); + const [profile, setProfile] = useState(); - const profile = useObservableMemo( - () => eventStore.model(ProfileModel, pubkey), - [eventStore, pubkey], - ); - - // Fetch profile if not in store (only runs once per pubkey) useEffect(() => { - if (profile) return; // Already have the event + let mounted = true; + // Load from IndexedDB first + db.profiles.get(pubkey).then((cachedProfile) => { + if (mounted && cachedProfile) { + setProfile(cachedProfile); + } + }); + + // Fetch from network const sub = profileLoader({ kind: kinds.Metadata, pubkey }).subscribe({ - next: (fetchedEvent) => { - if (fetchedEvent) { - eventStore.add(fetchedEvent); + next: async (fetchedEvent) => { + if (!fetchedEvent || !fetchedEvent.content) return; + + try { + const profileData = JSON.parse(fetchedEvent.content) as ProfileContent; + + // Save to IndexedDB + await db.profiles.put({ + ...profileData, + pubkey, + created_at: fetchedEvent.created_at, + }); + + if (mounted) { + setProfile(profileData); + } + } catch (e) { + console.error("[useProfile] Failed to parse profile:", e); } }, + error: (err) => { + console.error("[useProfile] Error fetching profile:", err); + }, }); - return () => sub.unsubscribe(); - }, [pubkey, eventStore]); // Removed event and loading from deps + return () => { + mounted = false; + sub.unsubscribe(); + }; + }, [pubkey]); return profile; } diff --git a/src/services/db.ts b/src/services/db.ts index 6e465de..498e3b5 100644 --- a/src/services/db.ts +++ b/src/services/db.ts @@ -2,6 +2,7 @@ import { ProfileContent } from "applesauce-core/helpers"; import { Dexie, Table } from "dexie"; export interface Profile extends ProfileContent { + pubkey: string; created_at: number; }