From 55a49b442baa850a02928fde03873a7ca1e596bf Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 00:13:58 -0500 Subject: [PATCH] Feed: 'My notes' scope with per-profile dropdown filter --- frontend/src/lib/api.ts | 3 +- frontend/src/screens/FeedScreen.tsx | 81 ++++++++++++++++++++++++--- frontend/src/state/AppProvider.tsx | 6 +- frontend/src/styles.css | 4 ++ frontend/src/test/FeedScreen.test.tsx | 51 +++++++++++++++++ frontend/src/test/fakeBackend.ts | 19 +++++++ src/feed.rs | 37 ++++++++++++ src/ipc.rs | 28 +++++++-- 8 files changed, 212 insertions(+), 17 deletions(-) diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index f5f78e4..ba56bd5 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -69,10 +69,11 @@ export const api = { nip05, }), publishNote: (content: string) => call('publish_note', { content }), - feedGet: (limit?: number, contactsOnly = false) => + feedGet: (limit?: number, contactsOnly = false, authorNpub?: string) => call('feed_get', { ...(limit ? { limit } : {}), ...(contactsOnly ? { contacts_only: true } : {}), + ...(authorNpub ? { author: authorNpub } : {}), }), relayAdd: (url: string) => call('relay_add', { url }), relayRemove: (url: string) => call('relay_remove', { url }), diff --git a/frontend/src/screens/FeedScreen.tsx b/frontend/src/screens/FeedScreen.tsx index 4dff080..816ba4d 100644 --- a/frontend/src/screens/FeedScreen.tsx +++ b/frontend/src/screens/FeedScreen.tsx @@ -15,21 +15,33 @@ interface FeedScreenProps { onNavigate: (screen: Screen) => void; } -type FeedScope = 'everyone' | 'contacts'; +type FeedScope = 'everyone' | 'contacts' | 'profile'; export function FeedScreen({ onNavigate }: FeedScreenProps) { const { state, feedGet } = useApp(); const [items, setItems] = useState([]); const [scope, setScope] = useState('everyone'); + const [pickedProfile, setPickedProfile] = useState(null); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const enabledRelays = (state?.settings.relays ?? []).filter((r) => r.enabled); const shorten = state?.settings.shorten_npub ?? true; + const profiles = state?.profiles ?? []; const hasActiveProfile = state?.active_profile != null; + const hasProfiles = profiles.length > 0; + // Default the author filter to the active profile until one is picked. + const selectedProfileNpub = + pickedProfile ?? state?.active_profile?.npub ?? profiles[0]?.npub ?? null; const contactsScope = scope === 'contacts'; - const effectiveScope: FeedScope = contactsScope && !hasActiveProfile ? 'everyone' : scope; + const profileScope = scope === 'profile'; + const effectiveScope: FeedScope = + contactsScope && !hasActiveProfile + ? 'everyone' + : profileScope && !hasProfiles + ? 'everyone' + : scope; const load = useCallback( async (background: boolean) => { @@ -40,7 +52,13 @@ export function FeedScreen({ onNavigate }: FeedScreenProps) { setLoading(true); } try { - setItems(await feedGet(undefined, effectiveScope === 'contacts')); + setItems( + await feedGet( + undefined, + effectiveScope === 'contacts', + effectiveScope === 'profile' ? (selectedProfileNpub ?? undefined) : undefined, + ), + ); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { @@ -48,7 +66,7 @@ export function FeedScreen({ onNavigate }: FeedScreenProps) { setRefreshing(false); } }, - [feedGet, effectiveScope], + [feedGet, effectiveScope, selectedProfileNpub], ); useEffect(() => { @@ -122,7 +140,37 @@ export function FeedScreen({ onNavigate }: FeedScreenProps) { My contacts + + {effectiveScope === 'profile' && ( + + )}