diff options
author | Samuel Newman <mozzius@protonmail.com> | 2025-04-03 03:21:15 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-02 17:21:15 -0700 |
commit | 87da619aaa92e0ec762e68c13b24e58a25da10a8 (patch) | |
tree | 4da902d3ca43a226f6da8e5c090ab33c2df3297a /src/screens/Search/modules/ExploreSuggestedAccounts.tsx | |
parent | 8d1f97b5ffac5d86762f1d4e9384ff3097acbc52 (diff) | |
download | voidsky-87da619aaa92e0ec762e68c13b24e58a25da10a8.tar.zst |
[Explore] Base (#8053)
* migrate to #/screens * rm unneeded import * block drawer gesture on recent profiles * rm recommendations (#8056) * [Explore] Disable Trending videos (#8054) * remove giant header * disable * [Explore] Dynamic module ordering (#8066) * Dynamic module ordering * [Explore] New headers, metrics (#8067) * new sticky headers * improve spacing between modules * view metric on modules * update metrics names * [Explore] Suggested accounts module (#8072) * use modern profile card, update load more * add tab bar * tabbed suggested accounts * [Explore] Discover feeds module (#8073) * cap number of feeds to 3 * change feed pin button * Apply suggestions from code review Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * restore statsig to log events * filter out followed profiles, make suer enough are loaded (#8090) * [Explore] Trending topics (#8055) * redesigned trending topics * rm borders on web * get post count / age / ranking from api * spacing tweaks * fetch more topics then slice * use api data for avis/category * rm top border * Integrate new SDK, part out components * Clean up * Use status field * Bump SDK * Send up interests and langs --------- Co-authored-by: Eric Bailey <git@esb.lol> * Clean up module spacing and borders (cherry picked from commit 63d19b6c2d67e226e0e14709b1047a1f88b3ce1c) (cherry picked from commit 62d7d394ab1dc31b40b9c2cf59075adbf94737a1) * Switch back border ordering (cherry picked from commit 34e3789f8b410132c1390df3c2bb8257630ebdd9) * [Explore] Starter Packs (#8095) * Temp WIP (cherry picked from commit 43b5d7b1e64b3adb1ed162262d0310e0bf026c18) * New SP card * Load state * Revert change * Cleanup * Interests and caching * Count total * Format * Caching * [Explore] Feed previews module (#8075) * wip new hook * get fetching working, maybe * get feed previews rendering! * fix header height * working pin button * extract out FeedLink * add loader * only make preview:header sticky * Fix headers * Header tweaks * Fix moderation filter * Fix threading --------- Co-authored-by: Eric Bailey <git@esb.lol> * Space it out * Fix query key * Mock new endpoint, filter saved feeds * Make sure we're pinning, lower cache time * add news category * Remove log * Improve suggested accounts load state * Integrate new app view endpoint * fragment * Update src/screens/Search/modules/ExploreTrendingTopics.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/screens/Search/modules/ExploreTrendingTopics.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * lint * maybe fix this --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> Co-authored-by: Eric Bailey <git@esb.lol> Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src/screens/Search/modules/ExploreSuggestedAccounts.tsx')
-rw-r--r-- | src/screens/Search/modules/ExploreSuggestedAccounts.tsx | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/src/screens/Search/modules/ExploreSuggestedAccounts.tsx b/src/screens/Search/modules/ExploreSuggestedAccounts.tsx new file mode 100644 index 000000000..070d75910 --- /dev/null +++ b/src/screens/Search/modules/ExploreSuggestedAccounts.tsx @@ -0,0 +1,228 @@ +import {memo, useEffect} from 'react' +import {View} from 'react-native' +import {type AppBskyActorSearchActors, type ModerationOpts} from '@atproto/api' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {type InfiniteData} from '@tanstack/react-query' + +import {logger} from '#/logger' +import {usePreferencesQuery} from '#/state/queries/preferences' +import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture' +import { + popularInterests, + useInterestsDisplayNames, +} from '#/screens/Onboarding/state' +import {useTheme} from '#/alf' +import {atoms as a} from '#/alf' +import {Button} from '#/components/Button' +import * as ProfileCard from '#/components/ProfileCard' +import {boostInterests, Tabs} from '#/components/ProgressGuide/FollowDialog' +import {Text} from '#/components/Typography' +import type * as bsky from '#/types/bsky' + +export function useLoadEnoughProfiles({ + interest, + data, + isLoading, + isFetchingNextPage, + hasNextPage, + fetchNextPage, +}: { + interest: string | null + data?: InfiniteData<AppBskyActorSearchActors.OutputSchema> + isLoading: boolean + isFetchingNextPage: boolean + hasNextPage: boolean + fetchNextPage: () => Promise<unknown> +}) { + const profileCount = + data?.pages.flatMap(page => + page.actors.filter(actor => !actor.viewer?.following), + ).length || 0 + const isAnyLoading = isLoading || isFetchingNextPage + const isEnoughProfiles = profileCount > 3 + const shouldFetchMore = !isEnoughProfiles && hasNextPage && !!interest + useEffect(() => { + if (shouldFetchMore && !isAnyLoading) { + logger.info('Not enough suggested accounts - fetching more') + fetchNextPage() + } + }, [shouldFetchMore, fetchNextPage, isAnyLoading, interest]) + + return { + isReady: !shouldFetchMore, + } +} + +export function SuggestedAccountsTabBar({ + selectedInterest, + onSelectInterest, +}: { + selectedInterest: string | null + onSelectInterest: (interest: string | null) => void +}) { + const {_} = useLingui() + const interestsDisplayNames = useInterestsDisplayNames() + const {data: preferences} = usePreferencesQuery() + const personalizedInterests = preferences?.interests?.tags + const interests = Object.keys(interestsDisplayNames) + .sort(boostInterests(popularInterests)) + .sort(boostInterests(personalizedInterests)) + return ( + <BlockDrawerGesture> + <Tabs + interests={['all', ...interests]} + selectedInterest={selectedInterest || 'all'} + onSelectTab={tab => { + logger.metric( + 'explore:suggestedAccounts:tabPressed', + {tab: tab}, + {statsig: true}, + ) + onSelectInterest(tab === 'all' ? null : tab) + }} + hasSearchText={false} + interestsDisplayNames={{ + all: _(msg`All`), + ...interestsDisplayNames, + }} + TabComponent={Tab} + /> + </BlockDrawerGesture> + ) +} + +let Tab = ({ + onSelectTab, + interest, + active, + index, + interestsDisplayName, + onLayout, +}: { + onSelectTab: (index: number) => void + interest: string + active: boolean + index: number + interestsDisplayName: string + onLayout: (index: number, x: number, width: number) => void +}): React.ReactNode => { + const t = useTheme() + const {_} = useLingui() + const activeText = active ? _(msg` (active)`) : '' + return ( + <View + key={interest} + onLayout={e => + onLayout(index, e.nativeEvent.layout.x, e.nativeEvent.layout.width) + }> + <Button + label={_(msg`Search for "${interestsDisplayName}"${activeText}`)} + onPress={() => onSelectTab(index)}> + {({hovered, pressed, focused}) => ( + <View + style={[ + a.rounded_full, + a.px_lg, + a.py_sm, + a.border, + active || hovered || pressed || focused + ? [ + t.atoms.bg_contrast_25, + {borderColor: t.atoms.bg_contrast_25.backgroundColor}, + ] + : [t.atoms.bg, t.atoms.border_contrast_low], + ]}> + <Text + style={[ + /* TODO: medium weight */ + active || hovered || pressed || focused + ? t.atoms.text + : t.atoms.text_contrast_medium, + ]}> + {interestsDisplayName} + </Text> + </View> + )} + </Button> + </View> + ) +} +Tab = memo(Tab) + +/** + * Profile card for suggested accounts. Note: border is on the bottom edge + */ +let SuggestedProfileCard = ({ + profile, + moderationOpts, + recId, + position, +}: { + profile: bsky.profile.AnyProfileView + moderationOpts: ModerationOpts + recId?: number + position: number +}): React.ReactNode => { + const t = useTheme() + return ( + <ProfileCard.Link + profile={profile} + style={[a.flex_1]} + onPress={() => { + logger.metric( + 'suggestedUser:press', + { + logContext: 'Explore', + recId, + position, + }, + {statsig: true}, + ) + }}> + <View + style={[ + a.w_full, + a.py_lg, + a.px_lg, + a.border_t, + t.atoms.border_contrast_low, + a.flex_1, + ]}> + <ProfileCard.Outer> + <ProfileCard.Header> + <ProfileCard.Avatar + profile={profile} + moderationOpts={moderationOpts} + /> + <ProfileCard.NameAndHandle + profile={profile} + moderationOpts={moderationOpts} + /> + <ProfileCard.FollowButton + profile={profile} + moderationOpts={moderationOpts} + withIcon={false} + logContext="ExploreSuggestedAccounts" + onFollow={() => { + logger.metric( + 'suggestedUser:follow', + { + logContext: 'Explore', + location: 'Card', + recId, + position, + }, + {statsig: true}, + ) + }} + /> + </ProfileCard.Header> + <ProfileCard.Description profile={profile} numberOfLines={2} /> + </ProfileCard.Outer> + </View> + </ProfileCard.Link> + ) +} +SuggestedProfileCard = memo(SuggestedProfileCard) +export {SuggestedProfileCard} |