From f8a59e10dd347e9187aa4414a0ece04eff3b9bc3 Mon Sep 17 00:00:00 2001 From: Hailey Date: Mon, 8 Jul 2024 10:06:00 -0700 Subject: [PATCH 1/6] Fix RTL text rendering for display names (#4747) * header display name rtl support * highlighted post rtl * move `NON_BREAKING_SPACE` to an external constant * rtl support in search dropdown * profile card rtl * old profile card rtl * hover card * wizard list card * new chat * account card * chat header * clean up notifications * just force LTR on meta display name --- src/components/ProfileCard.tsx | 4 +++- src/components/ProfileHoverCard/index.web.tsx | 3 ++- .../StarterPack/Wizard/WizardListCard.tsx | 8 +++++++- src/components/dms/MessagesListHeader.tsx | 7 ++++++- .../dms/dialogs/SearchablePeopleList.tsx | 2 +- src/lib/strings/constants.ts | 1 + src/screens/Profile/Header/DisplayName.tsx | 2 +- src/view/com/notifications/FeedItem.tsx | 9 ++++++--- src/view/com/post-thread/PostThreadItem.tsx | 2 +- src/view/com/profile/ProfileCard.tsx | 2 +- src/view/com/util/PostMeta.tsx | 18 ++++++++---------- src/view/screens/Settings/index.tsx | 3 ++- src/view/shell/desktop/Search.tsx | 3 ++- 13 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 src/lib/strings/constants.ts diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx index 79f1108cb3..8bc9376e0c 100644 --- a/src/components/ProfileCard.tsx +++ b/src/components/ProfileCard.tsx @@ -166,7 +166,9 @@ export function NameAndHandle({ return ( - + {name} - + {sanitizeDisplayName( profile.displayName || sanitizeHandle(profile.handle), moderation.ui('displayName'), diff --git a/src/components/StarterPack/Wizard/WizardListCard.tsx b/src/components/StarterPack/Wizard/WizardListCard.tsx index aa1b2cf9bd..55cf0f02b3 100644 --- a/src/components/StarterPack/Wizard/WizardListCard.tsx +++ b/src/components/StarterPack/Wizard/WizardListCard.tsx @@ -78,7 +78,13 @@ function WizardListCard({ /> {displayName} diff --git a/src/components/dms/MessagesListHeader.tsx b/src/components/dms/MessagesListHeader.tsx index 8bf673d300..7b9f1a3a02 100644 --- a/src/components/dms/MessagesListHeader.tsx +++ b/src/components/dms/MessagesListHeader.tsx @@ -168,7 +168,12 @@ function HeaderReady({ {displayName} diff --git a/src/components/dms/dialogs/SearchablePeopleList.tsx b/src/components/dms/dialogs/SearchablePeopleList.tsx index d92ea68350..a13dfe509f 100644 --- a/src/components/dms/dialogs/SearchablePeopleList.tsx +++ b/src/components/dms/dialogs/SearchablePeopleList.tsx @@ -395,7 +395,7 @@ function ProfileCard({ /> {displayName} diff --git a/src/lib/strings/constants.ts b/src/lib/strings/constants.ts new file mode 100644 index 0000000000..4bce3ac626 --- /dev/null +++ b/src/lib/strings/constants.ts @@ -0,0 +1 @@ +export const NON_BREAKING_SPACE = '\u00A0' diff --git a/src/screens/Profile/Header/DisplayName.tsx b/src/screens/Profile/Header/DisplayName.tsx index c63658a44a..955e3d72c8 100644 --- a/src/screens/Profile/Header/DisplayName.tsx +++ b/src/screens/Profile/Header/DisplayName.tsx @@ -20,7 +20,7 @@ export function ProfileHeaderDisplayName({ + style={[t.atoms.text, a.text_4xl, a.self_start, {fontWeight: '500'}]}> {sanitizeDisplayName( profile.displayName || sanitizeHandle(profile.handle), moderation.ui('displayName'), diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx index 1932efbd5c..d31962ff35 100644 --- a/src/view/com/notifications/FeedItem.tsx +++ b/src/view/com/notifications/FeedItem.tsx @@ -58,6 +58,7 @@ import {useNavigation} from '@react-navigation/native' import {parseTenorGif} from '#/lib/strings/embed-player' import {logger} from '#/logger' import {NavigationProp} from 'lib/routes/types' +import {forceLTR} from 'lib/strings/bidi' import {DM_SERVICE_HEADERS} from 'state/queries/messages/const' import {useAgent} from 'state/session' import {Button, ButtonText} from '#/components/Button' @@ -274,13 +275,15 @@ let FeedItem = ({ showDmButton={item.type === 'starterpack-joined' || isFollowBack} /> - + diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 0f5350e790..4e6ed27bed 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -281,7 +281,7 @@ let PostThreadItemLoaded = ({ {sanitizeDisplayName( diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx index 7332d452ad..d18103f306 100644 --- a/src/view/com/profile/ProfileCard.tsx +++ b/src/view/com/profile/ProfileCard.tsx @@ -97,7 +97,7 @@ export function ProfileCard({ {sanitizeDisplayName( diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx index aec787e4e5..95168e8b3c 100644 --- a/src/view/com/util/PostMeta.tsx +++ b/src/view/com/util/PostMeta.tsx @@ -6,6 +6,8 @@ import {useQueryClient} from '@tanstack/react-query' import {precacheProfile} from '#/state/queries/profile' import {usePalette} from 'lib/hooks/usePalette' import {makeProfileLink} from 'lib/routes/links' +import {forceLTR} from 'lib/strings/bidi' +import {NON_BREAKING_SPACE} from 'lib/strings/constants' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' import {niceDate} from 'lib/strings/time' @@ -32,8 +34,6 @@ interface PostMetaOpts { style?: StyleProp } -const NON_BREAKING_SPACE = '\u00A0' - let PostMeta = (opts: PostMetaOpts): React.ReactNode => { const pal = usePalette('default') const displayName = opts.author.displayName || opts.author.handle @@ -70,14 +70,12 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { style={[pal.text]} lineHeight={1.2} disableMismatchWarning - text={ - <> - {sanitizeDisplayName( - displayName, - opts.moderation?.ui('displayName'), - )} - - } + text={forceLTR( + sanitizeDisplayName( + displayName, + opts.moderation?.ui('displayName'), + ), + )} href={profileLink} onBeforePress={onBeforePressAuthor} /> diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index 1d8199b009..f59bfb1834 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -68,6 +68,7 @@ import {navigate, resetToTab} from '#/Navigation' import {Email2FAToggle} from './Email2FAToggle' import {ExportCarDialog} from './ExportCarDialog' import hairlineWidth = StyleSheet.hairlineWidth +import {atoms as a} from '#/alf' function SettingsAccountCard({ account, @@ -104,7 +105,7 @@ function SettingsAccountCard({ /> - + {profile?.displayName || account.handle} diff --git a/src/view/shell/desktop/Search.tsx b/src/view/shell/desktop/Search.tsx index 3829a6c0b2..d8aa518996 100644 --- a/src/view/shell/desktop/Search.tsx +++ b/src/view/shell/desktop/Search.tsx @@ -30,6 +30,7 @@ import {precacheProfile} from 'state/queries/profile' import {Link} from '#/view/com/util/Link' import {UserAvatar} from '#/view/com/util/UserAvatar' import {Text} from 'view/com/util/text/Text' +import {atoms as a} from '#/alf' let SearchLinkCard = ({ label, @@ -127,7 +128,7 @@ let SearchProfileCard = ({ {sanitizeDisplayName( From a3c43a74712cdd402dc1e36c3c6b487ffaa1589d Mon Sep 17 00:00:00 2001 From: Hailey Date: Mon, 8 Jul 2024 15:16:43 -0700 Subject: [PATCH 2/6] change `contentVisibility` to `contain` (#4752) --- .../Messages/Conversation/MessagesList.tsx | 4 +- src/screens/StarterPack/Wizard/StepFeeds.tsx | 2 +- .../StarterPack/Wizard/StepProfiles.tsx | 2 +- src/view/com/util/List.tsx | 5 +- src/view/com/util/List.web.tsx | 47 ++++++++++--------- src/view/screens/Storybook/ListContained.tsx | 2 +- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/screens/Messages/Conversation/MessagesList.tsx b/src/screens/Messages/Conversation/MessagesList.tsx index f72515ac62..11b951e99d 100644 --- a/src/screens/Messages/Conversation/MessagesList.tsx +++ b/src/screens/Messages/Conversation/MessagesList.tsx @@ -386,10 +386,10 @@ export function MessagesList({ data={convoState.items} renderItem={renderItem} keyExtractor={keyExtractor} - containWeb={true} + disableFullWindowScroll={true} // Prevents wrong position in Firefox when sending a message // as well as scroll getting stuck on Chome when scrolling upwards. - disableContentVisibility={true} + disableContainStyle={true} disableVirtualization={true} style={animatedListStyle} // The extra two items account for the header and the footer components diff --git a/src/screens/StarterPack/Wizard/StepFeeds.tsx b/src/screens/StarterPack/Wizard/StepFeeds.tsx index 878d17ce01..de8d856aba 100644 --- a/src/screens/StarterPack/Wizard/StepFeeds.tsx +++ b/src/screens/StarterPack/Wizard/StepFeeds.tsx @@ -101,7 +101,7 @@ export function StepFeeds({moderationOpts}: {moderationOpts: ModerationOpts}) { onEndReachedThreshold={2} renderScrollComponent={props => } keyboardShouldPersistTaps="handled" - containWeb={true} + disableFullWindowScroll={true} sideBorders={false} style={{flex: 1}} ListEmptyComponent={ diff --git a/src/screens/StarterPack/Wizard/StepProfiles.tsx b/src/screens/StarterPack/Wizard/StepProfiles.tsx index f77a46e7ab..c14de847f6 100644 --- a/src/screens/StarterPack/Wizard/StepProfiles.tsx +++ b/src/screens/StarterPack/Wizard/StepProfiles.tsx @@ -80,7 +80,7 @@ export function StepProfiles({ keyExtractor={keyExtractor} renderScrollComponent={props => } keyboardShouldPersistTaps="handled" - containWeb={true} + disableFullWindowScroll={true} sideBorders={false} style={[a.flex_1]} onEndReached={ diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx index ed43ed5f0d..e1a10e4741 100644 --- a/src/view/com/util/List.tsx +++ b/src/view/com/util/List.tsx @@ -24,11 +24,12 @@ export type ListProps = Omit< refreshing?: boolean onRefresh?: () => void onItemSeen?: (item: ItemT) => void - containWeb?: boolean desktopFixedHeight?: number | boolean + // Web only prop to contain the scroll to the container rather than the window + disableFullWindowScroll?: boolean sideBorders?: boolean // Web only prop to disable a perf optimization (which would otherwise be on). - disableContentVisibility?: boolean + disableContainStyle?: boolean } export type ListRef = React.MutableRefObject diff --git a/src/view/com/util/List.web.tsx b/src/view/com/util/List.web.tsx index f2b2add377..12d223db03 100644 --- a/src/view/com/util/List.web.tsx +++ b/src/view/com/util/List.web.tsx @@ -23,9 +23,11 @@ export type ListProps = Omit< onRefresh?: () => void onItemSeen?: (item: ItemT) => void desktopFixedHeight?: number | boolean - containWeb?: boolean + // Web only prop to contain the scroll to the container rather than the window + disableFullWindowScroll?: boolean sideBorders?: boolean - disableContentVisibility?: boolean + // Web only prop to disable a perf optimization (which would otherwise be on). + disableContainStyle?: boolean } export type ListRef = React.MutableRefObject // TODO: Better types. @@ -39,7 +41,7 @@ function ListImpl( ListHeaderComponent, ListFooterComponent, ListEmptyComponent, - containWeb, + disableFullWindowScroll, contentContainerStyle, data, desktopFixedHeight, @@ -58,7 +60,7 @@ function ListImpl( extraData, style, sideBorders = true, - disableContentVisibility, + disableContainStyle, ...props }: ListProps, ref: React.Ref, @@ -112,7 +114,7 @@ function ListImpl( } const getScrollableNode = React.useCallback(() => { - if (containWeb) { + if (disableFullWindowScroll) { const element = nativeRef.current as HTMLDivElement | null if (!element) return @@ -182,7 +184,7 @@ function ListImpl( }, } } - }, [containWeb]) + }, [disableFullWindowScroll]) const nativeRef = React.useRef(null) React.useImperativeHandle( @@ -267,7 +269,12 @@ function ListImpl( return () => { element?.removeEventListener('scroll', handleScroll) } - }, [isInsideVisibleTree, handleScroll, containWeb, getScrollableNode]) + }, [ + isInsideVisibleTree, + handleScroll, + disableFullWindowScroll, + getScrollableNode, + ]) // --- onScrolledDownChange --- const isScrolledDown = useRef(false) @@ -308,7 +315,7 @@ function ListImpl( {...props} style={[ style, - containWeb && { + disableFullWindowScroll && { flex: 1, // @ts-expect-error web only 'overflow-y': 'scroll', @@ -332,13 +339,13 @@ function ListImpl( pal.border, ]}> {onStartReached && !isEmpty && ( @@ -356,13 +363,13 @@ function ListImpl( renderItem={renderItem} extraData={extraData} onItemSeen={onItemSeen} - disableContentVisibility={disableContentVisibility} + disableContainStyle={disableContainStyle} /> ) })} {onEndReached && !isEmpty && ( ({ renderItem, extraData: _unused, onItemSeen, - disableContentVisibility, + disableContainStyle, }: { item: ItemT index: number @@ -416,7 +423,7 @@ let Row = function RowImpl({ | ((data: {index: number; item: any; separators: any}) => React.ReactNode) extraData: any onItemSeen: ((item: any) => void) | undefined - disableContentVisibility?: boolean + disableContainStyle?: boolean }): React.ReactNode { const rowRef = React.useRef(null) const intersectionTimeout = React.useRef(undefined) @@ -465,14 +472,10 @@ let Row = function RowImpl({ return null } - const shouldDisableContentVisibility = disableContentVisibility || isSafari + const shouldDisableContainStyle = disableContainStyle || isSafari return ( {renderItem({item, index, separators: null as any})} @@ -544,9 +547,9 @@ const styles = StyleSheet.create({ marginLeft: 'auto', marginRight: 'auto', }, - contentVisibilityAuto: { + contain: { // @ts-ignore web only - contentVisibility: 'auto', + contain: 'layout paint', }, minHeightViewport: { // @ts-ignore web only diff --git a/src/view/screens/Storybook/ListContained.tsx b/src/view/screens/Storybook/ListContained.tsx index b3ea091f40..20ec686570 100644 --- a/src/view/screens/Storybook/ListContained.tsx +++ b/src/view/screens/Storybook/ListContained.tsx @@ -47,7 +47,7 @@ export function ListContained() { ) }} keyExtractor={item => item.id.toString()} - containWeb={true} + disableFullWindowScroll={true} style={{flex: 1}} onStartReached={() => { console.log('Start Reached') From ca0a3270521172f19315ac9c33a1a18b4d2b78e1 Mon Sep 17 00:00:00 2001 From: Hailey Date: Mon, 8 Jul 2024 18:16:34 -0700 Subject: [PATCH 3/6] tweak top padding external (#4755) --- src/view/com/util/post-embeds/index.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index 942ad57b81..3fd55f5c21 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -131,7 +131,7 @@ export function PostEmbeds({ const {alt, thumb, aspectRatio} = images[0] return ( - + - + - + ) } @@ -183,7 +187,7 @@ export function PostEmbeds({ } const styles = StyleSheet.create({ - imagesContainer: { + container: { marginTop: 8, }, altContainer: { From 4360087ced80a5633781cf6f1c307606a1b0a210 Mon Sep 17 00:00:00 2001 From: "A Malik. Muhd" Date: Tue, 9 Jul 2024 03:02:26 +0100 Subject: [PATCH 4/6] Update README.md (#4394) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 49c4b016ff..64bcbcea5e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Get the app itself: ## Development Resources -This is a [React Native](https://reactnative.dev/) application, written in the TypeScript programming language. It builds on the `atproto` TypeScript packages (like [`@atproto/api`](https://www.npmjs.com/package/@atproto/api)), code for which is also on open source, but in [a different git repository](https://github.com/bluesky-social/atproto). +This is a [React Native](https://reactnative.dev/) application, written in the TypeScript programming language. It builds on the `atproto` TypeScript packages (like [`@atproto/api`](https://www.npmjs.com/package/@atproto/api)), code for which is also open source, but in [a different git repository](https://github.com/bluesky-social/atproto). There is a small amount of Go language source code (in `./bskyweb/`), for a web service that returns the React Native Web application. @@ -42,10 +42,10 @@ The Bluesky Social application encompasses a set of schemas and APIs built in th - Open an issue and give some time for discussion before submitting a PR. - Stay away from PRs like... - Changing "Post" to "Skeet." - - Refactoring the codebase, eg to replace mobx with redux or something. + - Refactoring the codebase, e.g., to replace MobX with Redux or something. - Adding entirely new features without prior discussion. -Remember, we serve a wide community of users. Our day to day involves us constantly asking "which top priority is our top priority." If you submit well-written PRs that solve problems concisely, that's an awesome contribution. Otherwise, as much as we'd love to accept your ideas and contributions, we really don't have the bandwidth. That's what forking is for! +Remember, we serve a wide community of users. Our day-to-day involves us constantly asking "which top priority is our top priority." If you submit well-written PRs that solve problems concisely, that's an awesome contribution. Otherwise, as much as we'd love to accept your ideas and contributions, we really don't have the bandwidth. That's what forking is for! ## Forking guidelines @@ -63,7 +63,7 @@ If you discover any security issues, please send an email to security@bsky.app. ## Are you a developer interested in building on atproto? -Bluesky is an open social network built on the AT Protocol, a flexible technology that will never lock developers out of the ecosystems that they help build. With atproto, third-party can be as seamless as first-party through custom feeds, federated services, clients, and more. +Bluesky is an open social network built on the AT Protocol, a flexible technology that will never lock developers out of the ecosystems that they help build. With atproto, third-party integration can be as seamless as first-party through custom feeds, federated services, clients, and more. ## License (MIT) From 2d0eefebc338eee0d5d7e3e4c02bd6bba7f6baa0 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 9 Jul 2024 17:10:50 +0100 Subject: [PATCH 5/6] Add social proof to suggested follows (#4602) * replace unused `followers` prop with social proof * Introduce 'minimal' version * Gate social proof one explore page, fix space if no desc * Use smaller avis for minimal --------- Co-authored-by: Eric Bailey --- src/components/KnownFollowers.tsx | 20 ++++-- src/lib/statsig/gates.ts | 1 + src/view/com/profile/ProfileCard.tsx | 103 ++++++++++----------------- src/view/screens/Search/Explore.tsx | 15 +++- 4 files changed, 64 insertions(+), 75 deletions(-) diff --git a/src/components/KnownFollowers.tsx b/src/components/KnownFollowers.tsx index 7b861dc660..4017a7b0be 100644 --- a/src/components/KnownFollowers.tsx +++ b/src/components/KnownFollowers.tsx @@ -12,6 +12,7 @@ import {Link, LinkProps} from '#/components/Link' import {Text} from '#/components/Typography' const AVI_SIZE = 30 +const AVI_SIZE_SMALL = 20 const AVI_BORDER = 1 /** @@ -30,10 +31,12 @@ export function KnownFollowers({ profile, moderationOpts, onLinkPress, + minimal, }: { profile: AppBskyActorDefs.ProfileViewDetailed moderationOpts: ModerationOpts onLinkPress?: LinkProps['onPress'] + minimal?: boolean }) { const cache = React.useRef>( new Map(), @@ -59,6 +62,7 @@ export function KnownFollowers({ cachedKnownFollowers={cachedKnownFollowers} moderationOpts={moderationOpts} onLinkPress={onLinkPress} + minimal={minimal} /> ) } @@ -71,11 +75,13 @@ function KnownFollowersInner({ moderationOpts, cachedKnownFollowers, onLinkPress, + minimal, }: { profile: AppBskyActorDefs.ProfileViewDetailed moderationOpts: ModerationOpts cachedKnownFollowers: AppBskyActorDefs.KnownFollowers onLinkPress?: LinkProps['onPress'] + minimal?: boolean }) { const t = useTheme() const {_} = useLingui() @@ -110,6 +116,8 @@ function KnownFollowersInner({ */ if (slice.length === 0) return null + const SIZE = minimal ? AVI_SIZE_SMALL : AVI_SIZE + return ( @@ -129,8 +137,8 @@ function KnownFollowersInner({ diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index 378b273494..1c86d01da4 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -1,6 +1,7 @@ export type Gate = // Keep this alphabetic please. | 'debug_show_feedcontext' + | 'explore_page_profile_card_social_proof' | 'native_pwi_disabled' | 'new_user_guided_tour' | 'new_user_progress_guide' diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx index d18103f306..9458fbaf8e 100644 --- a/src/view/com/profile/ProfileCard.tsx +++ b/src/view/com/profile/ProfileCard.tsx @@ -5,7 +5,6 @@ import { moderateProfile, ModerationDecision, } from '@atproto/api' -import {Trans} from '@lingui/macro' import {useQueryClient} from '@tanstack/react-query' import {useProfileShadow} from '#/state/cache/profile-shadow' @@ -19,12 +18,16 @@ import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' import {s} from 'lib/styles' import {precacheProfile} from 'state/queries/profile' +import {atoms as a} from '#/alf' +import { + KnownFollowers, + shouldShowKnownFollowers, +} from '#/components/KnownFollowers' import {Link} from '../util/Link' import {Text} from '../util/text/Text' import {PreviewableUserAvatar} from '../util/UserAvatar' import {FollowButton} from './FollowButton' import hairlineWidth = StyleSheet.hairlineWidth -import {atoms as a} from '#/alf' import * as Pills from '#/components/Pills' export function ProfileCard({ @@ -33,22 +36,22 @@ export function ProfileCard({ noModFilter, noBg, noBorder, - followers, renderButton, onPress, style, + showKnownFollowers, }: { testID?: string profile: AppBskyActorDefs.ProfileViewBasic noModFilter?: boolean noBg?: boolean noBorder?: boolean - followers?: AppBskyActorDefs.ProfileView[] | undefined renderButton?: ( profile: Shadow, ) => React.ReactNode onPress?: () => void style?: StyleProp + showKnownFollowers?: boolean }) { const queryClient = useQueryClient() const pal = usePalette('default') @@ -70,6 +73,11 @@ export function ProfileCard({ return null } + const knownFollowersVisible = + showKnownFollowers && + shouldShowKnownFollowers(profile.viewer?.knownFollowers) && + moderationOpts + return ( {renderButton(profile)} ) : undefined} - {profile.description ? ( + {profile.description || knownFollowersVisible ? ( - - {profile.description as string} - + {profile.description ? ( + + {profile.description as string} + + ) : null} + {knownFollowersVisible ? ( + + + + ) : null} ) : null} - ) } @@ -155,73 +179,20 @@ export function ProfileCardPills({ ) } -function FollowersList({ - followers, -}: { - followers?: AppBskyActorDefs.ProfileView[] | undefined -}) { - const pal = usePalette('default') - const moderationOpts = useModerationOpts() - - const followersWithMods = React.useMemo(() => { - if (!followers || !moderationOpts) { - return [] - } - - return followers - .map(f => ({ - f, - mod: moderateProfile(f, moderationOpts), - })) - .filter(({mod}) => !mod.ui('profileList').filter) - }, [followers, moderationOpts]) - - if (!followersWithMods?.length) { - return null - } - - return ( - - - - Followed by{' '} - {followersWithMods.map(({f}) => f.displayName || f.handle).join(', ')} - - - {followersWithMods.slice(0, 3).map(({f, mod}) => ( - - - - - - ))} - - ) -} - export function ProfileCardWithFollowBtn({ profile, noBg, noBorder, - followers, onPress, logContext = 'ProfileCard', + showKnownFollowers, }: { - profile: AppBskyActorDefs.ProfileViewBasic + profile: AppBskyActorDefs.ProfileView noBg?: boolean noBorder?: boolean - followers?: AppBskyActorDefs.ProfileView[] | undefined onPress?: () => void logContext?: 'ProfileCard' | 'StarterPackProfilesList' + showKnownFollowers?: boolean }) { const {currentAccount} = useSession() const isMe = profile.did === currentAccount?.did @@ -231,7 +202,6 @@ export function ProfileCardWithFollowBtn({ profile={profile} noBg={noBg} noBorder={noBorder} - followers={followers} renderButton={ isMe ? undefined @@ -240,6 +210,7 @@ export function ProfileCardWithFollowBtn({ ) } onPress={onPress} + showKnownFollowers={!isMe && showKnownFollowers} /> ) } diff --git a/src/view/screens/Search/Explore.tsx b/src/view/screens/Search/Explore.tsx index 85e8ffa4ec..05fd85effe 100644 --- a/src/view/screens/Search/Explore.tsx +++ b/src/view/screens/Search/Explore.tsx @@ -10,6 +10,7 @@ import { import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useGate} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {isWeb} from '#/platform/detection' import {useModerationOpts} from '#/state/preferences/moderation-opts' @@ -241,7 +242,7 @@ type ExploreScreenItems = | { type: 'profile' key: string - profile: AppBskyActorDefs.ProfileViewBasic + profile: AppBskyActorDefs.ProfileView } | { type: 'feed' @@ -291,6 +292,7 @@ export function Explore() { error: feedsError, fetchNextPage: fetchNextFeedsPage, } = useGetPopularFeedsQuery({limit: 10}) + const gate = useGate() const isLoadingMoreProfiles = isFetchingNextProfilesPage && !isLoadingProfiles const onLoadMoreProfiles = React.useCallback(async () => { @@ -492,7 +494,14 @@ export function Explore() { case 'profile': { return ( - + ) } @@ -555,7 +564,7 @@ export function Explore() { } } }, - [t, moderationOpts], + [t, moderationOpts, gate], ) return ( From 1d8e954eff0e881109574780cff72ae03484e1f7 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 9 Jul 2024 20:07:37 +0100 Subject: [PATCH 6/6] Log session events unconditionally --- src/state/session/agent.ts | 2 ++ src/state/session/logging.ts | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts index 4bcb4c11ca..d31b5588b4 100644 --- a/src/state/session/agent.ts +++ b/src/state/session/agent.ts @@ -12,6 +12,7 @@ import {tryFetchGates} from '#/lib/statsig/statsig' import {getAge} from '#/lib/strings/time' import {logger} from '#/logger' import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders' +import {addSessionEventLog} from './logging' import { configureModerationForAccount, configureModerationForGuest, @@ -194,6 +195,7 @@ async function prepareAgent( const account = agentToSessionAccountOrThrow(agent) agent.setPersistSessionHandler(event => { onSessionChange(agent, account.did, event) + addSessionEventLog(account.did, event) }) return {agent, account} } diff --git a/src/state/session/logging.ts b/src/state/session/logging.ts index 16aa66fe72..ab67785ca6 100644 --- a/src/state/session/logging.ts +++ b/src/state/session/logging.ts @@ -1,4 +1,4 @@ -import {AtpSessionData} from '@atproto/api' +import {AtpSessionData, AtpSessionEvent} from '@atproto/api' import {sha256} from 'js-sha256' import {Statsig} from 'statsig-react-native-expo' @@ -70,6 +70,18 @@ export function wrapSessionReducerForLogging(reducer: Reducer): Reducer { let nextMessageIndex = 0 const MAX_SLICE_LENGTH = 1000 +// Not gated. +export function addSessionEventLog(did: string, event: AtpSessionEvent) { + try { + if (!Statsig.initializeCalled() || !Statsig.getStableID()) { + return + } + Statsig.logEvent('session:event', null, {did, event}) + } catch (e) { + console.error(e) + } +} + export function addSessionDebugLog(log: Log) { try { if (!Statsig.initializeCalled() || !Statsig.getStableID()) {