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() }), }));