From 51a010bfc51d129ee530a1f9c5d8061bbd322fdb Mon Sep 17 00:00:00 2001 From: Jure <44338+hoornet@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:23:23 +0200 Subject: [PATCH] Mark new followers with badge, sort to top of follows list Track new follower pubkeys in notification store. When user opens the follows view, new followers are highlighted with an accent "new" badge and sorted to the top. Badge clears on view open (one-time). --- src/components/follows/FollowsView.tsx | 19 ++++++++++++++++--- src/lib/notificationPoller.ts | 2 +- src/stores/notifications.ts | 11 ++++++++--- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/components/follows/FollowsView.tsx b/src/components/follows/FollowsView.tsx index 1cdc496..1883cf0 100644 --- a/src/components/follows/FollowsView.tsx +++ b/src/components/follows/FollowsView.tsx @@ -11,9 +11,11 @@ import { shortenPubkey, profileName } from "../../lib/utils"; function FollowRow({ pubkey, followsYou, + isNew, }: { pubkey: string; followsYou?: boolean; + isNew?: boolean; }) { const profile = useProfile(pubkey); const name = profileName(profile, shortenPubkey(pubkey)); @@ -60,6 +62,9 @@ function FollowRow({ {followsYou && ( follows you )} + {isNew && ( + new + )} @@ -82,12 +87,14 @@ function FollowRow({ export function FollowsView() { const { followsTab, setFollowsTab } = useUIStore(); const { pubkey, follows } = useUserStore(); - const { clearNewFollowers } = useNotificationsStore(); + const { newFollowerPubkeys, clearNewFollowers } = useNotificationsStore(); const [followers, setFollowers] = useState([]); const [followersLoading, setFollowersLoading] = useState(false); const [followersError, setFollowersError] = useState(null); const [followersFetched, setFollowersFetched] = useState(false); + // Snapshot new follower pubkeys on mount, before clearing + const [newPubkeys] = useState(() => new Set(newFollowerPubkeys)); // Clear badge when view opens useEffect(() => { @@ -191,8 +198,14 @@ export function FollowsView() { {!followersLoading && !followersError && followers.length === 0 && followersFetched && (

No followers found yet.

)} - {followers.map((pk) => ( - + {[...followers] + .sort((a, b) => { + const aNew = newPubkeys.has(a) ? 1 : 0; + const bNew = newPubkeys.has(b) ? 1 : 0; + return bNew - aNew; // new followers first + }) + .map((pk) => ( + ))} )} diff --git a/src/lib/notificationPoller.ts b/src/lib/notificationPoller.ts index 582b449..5bb6a86 100644 --- a/src/lib/notificationPoller.ts +++ b/src/lib/notificationPoller.ts @@ -96,7 +96,7 @@ async function pollOnce(pubkey: string) { for (const e of newFollowers) { const name = await getProfileName(e.pubkey); notifyFollower(name).catch(() => {}); - useNotificationsStore.getState().incrementNewFollowers(); + useNotificationsStore.getState().addNewFollower(e.pubkey); } } } catch { /* non-critical */ } diff --git a/src/stores/notifications.ts b/src/stores/notifications.ts index aeab823..90c21d8 100644 --- a/src/stores/notifications.ts +++ b/src/stores/notifications.ts @@ -17,6 +17,7 @@ interface NotificationsState { dmLastSeen: Record; dmUnreadCount: number; newFollowersCount: number; + newFollowerPubkeys: Set; loadFromDb: (pubkey: string) => Promise; fetchNotifications: (pubkey: string) => Promise; @@ -25,7 +26,7 @@ interface NotificationsState { isRead: (eventId: string) => boolean; markDMRead: (partnerPubkey: string) => void; computeDMUnread: (conversations: Array<{ partnerPubkey: string; lastAt: number }>) => void; - incrementNewFollowers: () => void; + addNewFollower: (pubkey: string) => void; clearNewFollowers: () => void; } @@ -76,6 +77,7 @@ export const useNotificationsStore = create((set, get) => ({ dmLastSeen: loadDMLastSeen(), dmUnreadCount: 0, newFollowersCount: 0, + newFollowerPubkeys: new Set(), loadFromDb: async (pubkey: string) => { const isNewAccount = pubkey !== get().currentPubkey; @@ -210,6 +212,9 @@ export const useNotificationsStore = create((set, get) => ({ set({ dmUnreadCount }); }, - incrementNewFollowers: () => set((s) => ({ newFollowersCount: s.newFollowersCount + 1 })), - clearNewFollowers: () => set({ newFollowersCount: 0 }), + addNewFollower: (pubkey: string) => set((s) => ({ + newFollowersCount: s.newFollowersCount + 1, + newFollowerPubkeys: new Set([...s.newFollowerPubkeys, pubkey]), + })), + clearNewFollowers: () => set({ newFollowersCount: 0, newFollowerPubkeys: new Set() }), }));