From 818167a44ea9cb68bc7b3354e66698829511de4b Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sun, 29 Mar 2026 16:11:41 +0200 Subject: [PATCH] make sure that the profile tabs have mutually-exclusive contet --- .../Profile/ProfileFeedWithPins.tsx | 16 +++++++------- src/components/Profile/ProfileMediaFeed.tsx | 3 ++- src/constants.ts | 17 ++++++++++++--- .../primary/SpellsPage/fauxSpellFeeds.ts | 21 +++++++------------ 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/components/Profile/ProfileFeedWithPins.tsx b/src/components/Profile/ProfileFeedWithPins.tsx index faf31e74..e3aa36fa 100644 --- a/src/components/Profile/ProfileFeedWithPins.tsx +++ b/src/components/Profile/ProfileFeedWithPins.tsx @@ -1,7 +1,7 @@ import NoteCard from '@/components/NoteCard' import ProfileSearchBar from '@/components/ui/ProfileSearchBar' import { Skeleton } from '@/components/ui/skeleton' -import { ExtendedKind, PROFILE_POSTS_TAB_KINDS, PROFILE_PUBLICATIONS_TAB_KINDS } from '@/constants' +import { ExtendedKind, PROFILE_POSTS_TAB_KINDS } from '@/constants' import { isReplyNoteEvent } from '@/lib/event' import { getZapInfoFromEvent } from '@/lib/event-metadata' import { useProfilePins } from '@/hooks/useProfilePins' @@ -54,7 +54,6 @@ const ProfileFeedWithPins = forwardRef<{ refresh: () => void }, { pubkey: string return next.sort((a, b) => a - b) }, [showKinds]) const hideReplies = useHideRepliesLikeMainFeed() - const publicationsKindSet = useMemo(() => new Set(PROFILE_PUBLICATIONS_TAB_KINDS), []) const [searchQuery, setSearchQuery] = useState('') const [isRefreshing, setIsRefreshing] = useState(false) const [showCount, setShowCount] = useState(INITIAL_SHOW_COUNT) @@ -75,7 +74,11 @@ const ProfileFeedWithPins = forwardRef<{ refresh: () => void }, { pubkey: string [zapReplyThreshold] ) - const cacheKey = useMemo(() => `${pubkey}-profile-unified-${zapReplyThreshold}`, [pubkey, zapReplyThreshold]) + /** Bump when posts-tab `kinds` change so in-memory timeline cache is not reused across incompatible filters. */ + const cacheKey = useMemo( + () => `${pubkey}-profile-posts-tab-v2-${zapReplyThreshold}`, + [pubkey, zapReplyThreshold] + ) const postsTabKinds = useMemo(() => [...PROFILE_POSTS_TAB_KINDS], []) @@ -163,11 +166,8 @@ const ProfileFeedWithPins = forwardRef<{ refresh: () => void }, { pubkey: string ) const filteredPins = useMemo( - () => - applySearch(pinEvents) - .filter((e) => !isEventDeleted(e)) - .filter((e) => !publicationsKindSet.has(e.kind)), - [pinEvents, applySearch, isEventDeleted, publicationsKindSet] + () => applySearch(pinEvents).filter((e) => !isEventDeleted(e)), + [pinEvents, applySearch, isEventDeleted] ) const filteredRest = useMemo( () => diff --git a/src/components/Profile/ProfileMediaFeed.tsx b/src/components/Profile/ProfileMediaFeed.tsx index 8ae03dbd..698df86c 100644 --- a/src/components/Profile/ProfileMediaFeed.tsx +++ b/src/components/Profile/ProfileMediaFeed.tsx @@ -3,7 +3,8 @@ import { buildAuthorInboxOutboxRelayUrls } from '@/lib/favorites-feed-relays' import logger from '@/lib/logger' import { normalizeHexPubkey } from '@/lib/pubkey' import { computeSpellSubRequestsIdentityKey } from '@/lib/spell-feed-request-identity' -import { buildProfileMediaSubRequests, PROFILE_MEDIA_TAB_KINDS } from '@/pages/primary/SpellsPage/fauxSpellFeeds' +import { PROFILE_MEDIA_TAB_KINDS } from '@/constants' +import { buildProfileMediaSubRequests } from '@/pages/primary/SpellsPage/fauxSpellFeeds' import { normalizeUrl } from '@/lib/url' import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' import client from '@/services/client.service' diff --git a/src/constants.ts b/src/constants.ts index afa66ff4..10f71d98 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -506,12 +506,23 @@ export const PROFILE_PUBLICATIONS_TAB_KINDS: readonly number[] = [ const PROFILE_PUBLICATIONS_TAB_KIND_SET = new Set(PROFILE_PUBLICATIONS_TAB_KINDS) +/** NIP native media kinds for the profile Media tab (and Spells → media faux spell). */ +export const PROFILE_MEDIA_TAB_KINDS: readonly number[] = [ + ExtendedKind.PICTURE, + ExtendedKind.VIDEO, + ExtendedKind.SHORT_VIDEO, + ExtendedKind.VOICE +] + +const PROFILE_MEDIA_TAB_KIND_SET = new Set(PROFILE_MEDIA_TAB_KINDS) + /** - * Kinds subscribed on the profile Posts tab only. Omits {@link PROFILE_PUBLICATIONS_TAB_KINDS} so those events - * appear on the dedicated tab; {@link PROFILE_FEED_KINDS} is unchanged for the home feed and kind-filter defaults. + * Kinds subscribed on the profile Posts tab only. Omits publication kinds and native media kinds so those + * events appear only on Articles/Publications and Media; {@link PROFILE_FEED_KINDS} is unchanged for the home + * feed and kind-filter defaults. */ export const PROFILE_POSTS_TAB_KINDS: readonly number[] = PROFILE_FEED_KINDS.filter( - (k) => !PROFILE_PUBLICATIONS_TAB_KIND_SET.has(k) + (k) => !PROFILE_PUBLICATIONS_TAB_KIND_SET.has(k) && !PROFILE_MEDIA_TAB_KIND_SET.has(k) ) /** diff --git a/src/pages/primary/SpellsPage/fauxSpellFeeds.ts b/src/pages/primary/SpellsPage/fauxSpellFeeds.ts index 99d79366..9e855553 100644 --- a/src/pages/primary/SpellsPage/fauxSpellFeeds.ts +++ b/src/pages/primary/SpellsPage/fauxSpellFeeds.ts @@ -9,7 +9,12 @@ * inbox+favorites fill the cap and global kinds/media/hashtags never hit aggr). The **interests** spell * uses **one** shard: all subscribed topics in one `#t` filter (NIP-01 OR semantics). */ -import { DEFAULT_FEED_SHOW_KINDS, ExtendedKind, READ_ONLY_RELAY_URLS } from '@/constants' +import { + DEFAULT_FEED_SHOW_KINDS, + ExtendedKind, + PROFILE_MEDIA_TAB_KINDS, + READ_ONLY_RELAY_URLS +} from '@/constants' import { RENDERABLE_NOTE_KINDS_SORTED } from '@/lib/note-renderable-kinds' import { buildProfileAugmentedReadRelayUrls } from '@/lib/favorites-feed-relays' import { normalizeTopic } from '@/lib/discussion-topics' @@ -86,18 +91,8 @@ export function appendCuratedReadOnlyRelays(curated: string[], blockedRelays: st return out } -/** NIP-style native media kinds only (picture, video, short video, voice). */ -export const MEDIA_SPELL_KINDS = [ - ExtendedKind.PICTURE, - ExtendedKind.VIDEO, - ExtendedKind.SHORT_VIDEO, - ExtendedKind.VOICE -] as const - -/** - * Profile Medien tab: NIP native media only (picture, video, short video, voice) — same as {@link MEDIA_SPELL_KINDS}. - */ -export const PROFILE_MEDIA_TAB_KINDS = [...MEDIA_SPELL_KINDS] as const +/** NIP-style native media kinds only — same as {@link PROFILE_MEDIA_TAB_KINDS}. */ +export const MEDIA_SPELL_KINDS = PROFILE_MEDIA_TAB_KINDS function normalizeMentionPubkey(pubkey: string): string { return /^[0-9a-f]{64}$/i.test(pubkey.trim()) ? pubkey.trim().toLowerCase() : pubkey.trim()