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;
}