diff options
author | dan <dan.abramov@gmail.com> | 2024-12-13 21:38:43 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-13 21:38:43 +0000 |
commit | da6bcc54cfff198e629a34987746028414867ec9 (patch) | |
tree | cedbce914a1118db78f305189112ab0f0c76244c /src/view/screens/ProfileList.tsx | |
parent | adbc27059a6e7f59ad86f3df52038a163ee21981 (diff) | |
download | voidsky-da6bcc54cfff198e629a34987746028414867ec9.tar.zst |
Tweak ProfileList design (#7100)
* Remove "No description" * Move Lists about into header * Remove pager with one tab * Layout tweaks
Diffstat (limited to 'src/view/screens/ProfileList.tsx')
-rw-r--r-- | src/view/screens/ProfileList.tsx | 317 |
1 files changed, 128 insertions, 189 deletions
diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index 27ede80a3..2e661ff46 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -1,5 +1,6 @@ import React, {useCallback, useMemo} from 'react' import {Pressable, StyleSheet, View} from 'react-native' +import {useAnimatedRef} from 'react-native-reanimated' import { AppBskyGraphDefs, AtUri, @@ -19,12 +20,11 @@ import {usePalette} from '#/lib/hooks/usePalette' import {useSetTitle} from '#/lib/hooks/useSetTitle' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {ComposeIcon2} from '#/lib/icons' -import {makeListLink, makeProfileLink} from '#/lib/routes/links' +import {makeListLink} from '#/lib/routes/links' import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' import {NavigationProp} from '#/lib/routes/types' import {shareUrl} from '#/lib/sharing' import {cleanError} from '#/lib/strings/errors' -import {sanitizeHandle} from '#/lib/strings/handles' import {toShareUrl} from '#/lib/strings/url-helpers' import {s} from '#/lib/styles' import {logger} from '#/logger' @@ -63,14 +63,13 @@ import { DropdownItem, NativeDropdown, } from '#/view/com/util/forms/NativeDropdown' -import {TextLink} from '#/view/com/util/Link' import {ListRef} from '#/view/com/util/List' import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn' import {LoadingScreen} from '#/view/com/util/LoadingScreen' import {Text} from '#/view/com/util/text/Text' import * as Toast from '#/view/com/util/Toast' import {ListHiddenScreen} from '#/screens/List/ListHiddenScreen' -import {atoms as a, useTheme} from '#/alf' +import {atoms as a} from '#/alf' import {useDialogControl} from '#/components/Dialog' import * as Layout from '#/components/Layout' import * as Hider from '#/components/moderation/Hider' @@ -78,8 +77,7 @@ import * as Prompt from '#/components/Prompt' import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog' import {RichText} from '#/components/RichText' -const SECTION_TITLES_CURATE = ['Posts', 'About'] -const SECTION_TITLES_MOD = ['About'] +const SECTION_TITLES_CURATE = ['Posts', 'People'] interface SectionRef { scrollToTop: () => void @@ -161,6 +159,7 @@ function ProfileListScreenLoaded({ const isScreenFocused = useIsFocused() const isHidden = list.labels?.findIndex(l => l.val === '!hide') !== -1 const isOwner = currentAccount?.did === list.creator.did + const scrollElRef = useAnimatedRef() const moderation = React.useMemo(() => { return moderateUserList(list, moderationOpts) @@ -259,19 +258,13 @@ function ProfileListScreenLoaded({ </Hider.Mask> <Hider.Content> <View style={s.hContentRegion}> - <PagerWithHeader - items={SECTION_TITLES_MOD} - isHeaderReady={true} - renderHeader={renderHeader}> - {({headerHeight, scrollElRef}) => ( - <AboutSection - list={list} - scrollElRef={scrollElRef as ListRef} - onPressAddUser={onPressAddUser} - headerHeight={headerHeight} - /> - )} - </PagerWithHeader> + <Layout.Center>{renderHeader()}</Layout.Center> + <AboutSection + list={list} + scrollElRef={scrollElRef as ListRef} + onPressAddUser={onPressAddUser} + headerHeight={0} + /> <FAB testID="composeFAB" onPress={() => openComposer({})} @@ -652,101 +645,124 @@ function Header({ ] }, [_, subscribeMutePromptControl.open, subscribeBlockPromptControl.open]) + const descriptionRT = useMemo( + () => + list.description + ? new RichTextAPI({ + text: list.description, + facets: list.descriptionFacets, + }) + : undefined, + [list], + ) + return ( - <ProfileSubpageHeader - href={makeListLink(list.creator.handle || list.creator.did || '', rkey)} - title={list.name} - avatar={list.avatar} - isOwner={list.creator.did === currentAccount?.did} - creator={list.creator} - avatarType="list"> - <ReportDialog - control={reportDialogControl} - params={{ - type: 'list', - uri: list.uri, - cid: list.cid, - }} - /> - {isCurateList ? ( - <Button - testID={isPinned ? 'unpinBtn' : 'pinBtn'} - type={isPinned ? 'default' : 'inverted'} - label={isPinned ? _(msg`Unpin`) : _(msg`Pin to home`)} - onPress={onTogglePinned} - disabled={isPending} + <> + <ProfileSubpageHeader + href={makeListLink(list.creator.handle || list.creator.did || '', rkey)} + title={list.name} + avatar={list.avatar} + isOwner={list.creator.did === currentAccount?.did} + creator={list.creator} + purpose={list.purpose} + avatarType="list"> + <ReportDialog + control={reportDialogControl} + params={{ + type: 'list', + uri: list.uri, + cid: list.cid, + }} /> - ) : isModList ? ( - isBlocking ? ( + {isCurateList ? ( <Button - testID="unblockBtn" - type="default" - label={_(msg`Unblock`)} - onPress={onUnsubscribeBlock} + testID={isPinned ? 'unpinBtn' : 'pinBtn'} + type={isPinned ? 'default' : 'inverted'} + label={isPinned ? _(msg`Unpin`) : _(msg`Pin to home`)} + onPress={onTogglePinned} + disabled={isPending} /> - ) : isMuting ? ( - <Button - testID="unmuteBtn" - type="default" - label={_(msg`Unmute`)} - onPress={onUnsubscribeMute} - /> - ) : ( - <NativeDropdown - testID="subscribeBtn" - items={subscribeDropdownItems} - accessibilityLabel={_(msg`Subscribe to this list`)} - accessibilityHint=""> - <View style={[palInverted.view, styles.btn]}> - <Text style={palInverted.text}> - <Trans>Subscribe</Trans> - </Text> - </View> - </NativeDropdown> - ) - ) : null} - <NativeDropdown - testID="headerDropdownBtn" - items={dropdownItems} - accessibilityLabel={_(msg`More options`)} - accessibilityHint=""> - <View style={[pal.viewLight, styles.btn]}> - <FontAwesomeIcon icon="ellipsis" size={20} color={pal.colors.text} /> - </View> - </NativeDropdown> + ) : isModList ? ( + isBlocking ? ( + <Button + testID="unblockBtn" + type="default" + label={_(msg`Unblock`)} + onPress={onUnsubscribeBlock} + /> + ) : isMuting ? ( + <Button + testID="unmuteBtn" + type="default" + label={_(msg`Unmute`)} + onPress={onUnsubscribeMute} + /> + ) : ( + <NativeDropdown + testID="subscribeBtn" + items={subscribeDropdownItems} + accessibilityLabel={_(msg`Subscribe to this list`)} + accessibilityHint=""> + <View style={[palInverted.view, styles.btn]}> + <Text style={palInverted.text}> + <Trans>Subscribe</Trans> + </Text> + </View> + </NativeDropdown> + ) + ) : null} + <NativeDropdown + testID="headerDropdownBtn" + items={dropdownItems} + accessibilityLabel={_(msg`More options`)} + accessibilityHint=""> + <View style={[pal.viewLight, styles.btn]}> + <FontAwesomeIcon + icon="ellipsis" + size={20} + color={pal.colors.text} + /> + </View> + </NativeDropdown> - <Prompt.Basic - control={deleteListPromptControl} - title={_(msg`Delete this list?`)} - description={_( - msg`If you delete this list, you won't be able to recover it.`, - )} - onConfirm={onPressDelete} - confirmButtonCta={_(msg`Delete`)} - confirmButtonColor="negative" - /> + <Prompt.Basic + control={deleteListPromptControl} + title={_(msg`Delete this list?`)} + description={_( + msg`If you delete this list, you won't be able to recover it.`, + )} + onConfirm={onPressDelete} + confirmButtonCta={_(msg`Delete`)} + confirmButtonColor="negative" + /> - <Prompt.Basic - control={subscribeMutePromptControl} - title={_(msg`Mute these accounts?`)} - description={_( - msg`Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them.`, - )} - onConfirm={onSubscribeMute} - confirmButtonCta={_(msg`Mute list`)} - /> + <Prompt.Basic + control={subscribeMutePromptControl} + title={_(msg`Mute these accounts?`)} + description={_( + msg`Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them.`, + )} + onConfirm={onSubscribeMute} + confirmButtonCta={_(msg`Mute list`)} + /> - <Prompt.Basic - control={subscribeBlockPromptControl} - title={_(msg`Block these accounts?`)} - description={_( - msg`Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, - )} - onConfirm={onSubscribeBlock} - confirmButtonCta={_(msg`Block list`)} - confirmButtonColor="negative" - /> - </ProfileSubpageHeader> + <Prompt.Basic + control={subscribeBlockPromptControl} + title={_(msg`Block these accounts?`)} + description={_( + msg`Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, + )} + onConfirm={onSubscribeBlock} + confirmButtonCta={_(msg`Block list`)} + confirmButtonColor="negative" + /> + </ProfileSubpageHeader> + {descriptionRT ? ( + <View style={[a.px_lg, a.pt_sm, a.pb_sm, a.gap_md]}> + <RichText value={descriptionRT} style={[a.text_md, a.leading_snug]} /> + </View> + ) : null} + </> ) } @@ -825,25 +841,12 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( ref, ) { const pal = usePalette('default') - const t = useTheme() const {_} = useLingui() const {isMobile} = useWebMediaQueries() const {currentAccount} = useSession() const [isScrolledDown, setIsScrolledDown] = React.useState(false) - const isCurateList = list.purpose === 'app.bsky.graph.defs#curatelist' const isOwner = list.creator.did === currentAccount?.did - const descriptionRT = useMemo( - () => - list.description - ? new RichTextAPI({ - text: list.description, - facets: list.descriptionFacets, - }) - : undefined, - [list], - ) - const onScrollToTop = useCallback(() => { scrollElRef.current?.scrollToOffset({ animated: isNative, @@ -856,59 +859,11 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( })) const renderHeader = React.useCallback(() => { + if (!isOwner) { + return <View /> + } return ( - <View> - <View - style={[ - { - borderTopWidth: StyleSheet.hairlineWidth, - padding: isMobile ? 14 : 20, - gap: 12, - }, - pal.border, - ]}> - {descriptionRT ? ( - <RichText - testID="listDescription" - style={[a.text_md]} - value={descriptionRT} - /> - ) : ( - <Text - testID="listDescriptionEmpty" - type="lg" - style={[{fontStyle: 'italic'}, pal.textLight]}> - <Trans>No description</Trans> - </Text> - )} - <Text type="md" style={[pal.textLight]} numberOfLines={1}> - {isCurateList ? ( - isOwner ? ( - <Trans>User list by you</Trans> - ) : ( - <Trans> - User list by{' '} - <TextLink - text={sanitizeHandle(list.creator.handle || '', '@')} - href={makeProfileLink(list.creator)} - style={pal.textLight} - /> - </Trans> - ) - ) : isOwner ? ( - <Trans>Moderation list by you</Trans> - ) : ( - <Trans> - Moderation list by{' '} - <TextLink - text={sanitizeHandle(list.creator.handle || '', '@')} - href={makeProfileLink(list.creator)} - style={pal.textLight} - /> - </Trans> - )} - </Text> - </View> + <View style={a.pt_lg}> <View style={[ { @@ -919,9 +874,6 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( paddingBottom: isMobile ? 14 : 18, }, ]}> - <Text type="lg-bold" style={t.atoms.text}> - <Trans>Users</Trans> - </Text> {isOwner && ( <Pressable testID="addUserBtn" @@ -943,20 +895,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( </View> </View> ) - }, [ - isMobile, - pal.border, - pal.textLight, - pal.colors.link, - pal.link, - descriptionRT, - isCurateList, - isOwner, - list.creator, - t.atoms.text, - _, - onPressAddUser, - ]) + }, [isMobile, pal.colors.link, pal.link, isOwner, _, onPressAddUser]) const renderEmptyState = useCallback(() => { return ( |