From 060fa9802112baa81a66248a45c2f48dcf46d520 Mon Sep 17 00:00:00 2001 From: Hailey Date: Tue, 20 Aug 2024 15:32:18 -0700 Subject: [PATCH] List card hiding of title (#4964) * cleanup * hide title of moderation list * temp * clean * add margin * loosen types * tweak type * add an `override` prop to `hider` * Props switcheroo --------- Co-authored-by: Eric Bailey --- src/components/ListCard.tsx | 48 ++++- src/components/moderation/Hider.tsx | 20 +- .../moderation/ModerationDetailsDialog.tsx | 4 +- src/view/com/lists/ListCard.tsx | 183 ------------------ src/view/com/lists/MyLists.tsx | 40 ++-- src/view/com/util/post-embeds/ListEmbed.tsx | 32 --- src/view/com/util/post-embeds/index.tsx | 16 +- src/view/screens/ProfileList.tsx | 6 +- 8 files changed, 93 insertions(+), 256 deletions(-) delete mode 100644 src/view/com/lists/ListCard.tsx delete mode 100644 src/view/com/util/post-embeds/ListEmbed.tsx diff --git a/src/components/ListCard.tsx b/src/components/ListCard.tsx index 0ed27cf50f..829f36d471 100644 --- a/src/components/ListCard.tsx +++ b/src/components/ListCard.tsx @@ -1,13 +1,20 @@ import React from 'react' import {View} from 'react-native' -import {AppBskyActorDefs, AppBskyGraphDefs, AtUri} from '@atproto/api' +import { + AppBskyActorDefs, + AppBskyGraphDefs, + AtUri, + moderateUserList, + ModerationUI, +} from '@atproto/api' import {Trans} from '@lingui/macro' import {useQueryClient} from '@tanstack/react-query' import {sanitizeHandle} from 'lib/strings/handles' +import {useModerationOpts} from 'state/preferences/moderation-opts' import {precacheList} from 'state/queries/feed' -import {useTheme} from '#/alf' -import {atoms as a} from '#/alf' +import {useSession} from 'state/session' +import {atoms as a, useTheme} from '#/alf' import { Avatar, Description, @@ -16,6 +23,7 @@ import { SaveButton, } from '#/components/FeedCard' import {Link as InternalLink, LinkProps} from '#/components/Link' +import * as Hider from '#/components/moderation/Hider' import {Text} from '#/components/Typography' /* @@ -43,6 +51,11 @@ type Props = { export function Default(props: Props) { const {view, showPinButton} = props + const moderationOpts = useModerationOpts() + const moderation = moderationOpts + ? moderateUserList(view, moderationOpts) + : undefined + return ( @@ -52,6 +65,7 @@ export function Default(props: Props) { title={view.name} creator={view.creator} purpose={view.purpose} + modUi={moderation?.ui('contentView')} /> {showPinButton && view.purpose === CURATELIST && ( @@ -89,18 +103,40 @@ export function TitleAndByline({ title, creator, purpose = CURATELIST, + modUi, }: { title: string creator?: AppBskyActorDefs.ProfileViewBasic purpose?: AppBskyGraphDefs.ListView['purpose'] + modUi?: ModerationUI }) { const t = useTheme() + const {currentAccount} = useSession() return ( - - {title} - + + + + Hidden list + + + + + {title} + + + + {creator && ( void meta: { isNoPwi: boolean - noOverride: boolean + allowOverride: boolean } } @@ -27,24 +27,30 @@ export const useHider = () => React.useContext(Context) export function Outer({ modui, + isContentVisibleInitialState, + allowOverride, children, }: React.PropsWithChildren<{ - modui: ModerationUI + isContentVisibleInitialState?: boolean + allowOverride?: boolean + modui: ModerationUI | undefined }>) { const control = useModerationDetailsDialogControl() - const blur = modui.blurs[0] - const [isContentVisible, setIsContentVisible] = React.useState(!blur) + const blur = modui?.blurs[0] + const [isContentVisible, setIsContentVisible] = React.useState( + isContentVisibleInitialState || !blur, + ) const info = useModerationCauseDescription(blur) const meta = { isNoPwi: Boolean( - modui.blurs.find( + modui?.blurs.find( cause => cause.type === 'label' && cause.labelDef.identifier === '!no-unauthenticated', ), ), - noOverride: modui.noOverride, + allowOverride: allowOverride || !modui?.noOverride, } const showInfoDialog = () => { @@ -52,7 +58,7 @@ export function Outer({ } const onSetContentVisible = (show: boolean) => { - if (meta.noOverride) return + if (meta.allowOverride) return setIsContentVisible(show) } diff --git a/src/components/moderation/ModerationDetailsDialog.tsx b/src/components/moderation/ModerationDetailsDialog.tsx index ebfe452325..b8f02582c6 100644 --- a/src/components/moderation/ModerationDetailsDialog.tsx +++ b/src/components/moderation/ModerationDetailsDialog.tsx @@ -18,7 +18,7 @@ export {useDialogControl as useModerationDetailsDialogControl} from '#/component export interface ModerationDetailsDialogProps { control: Dialog.DialogOuterProps['control'] - modcause: ModerationCause + modcause?: ModerationCause } export function ModerationDetailsDialog(props: ModerationDetailsDialogProps) { @@ -123,7 +123,7 @@ function ModerationDetailsDialogInner({ {description} - {modcause.type === 'label' && ( + {modcause?.type === 'label' && ( <> diff --git a/src/view/com/lists/ListCard.tsx b/src/view/com/lists/ListCard.tsx deleted file mode 100644 index 587885502b..0000000000 --- a/src/view/com/lists/ListCard.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import React from 'react' -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' -import {AppBskyGraphDefs, AtUri, RichText} from '@atproto/api' -import {Trans} from '@lingui/macro' - -import {useSession} from '#/state/session' -import {usePalette} from 'lib/hooks/usePalette' -import {makeProfileLink} from 'lib/routes/links' -import {sanitizeDisplayName} from 'lib/strings/display-names' -import {sanitizeHandle} from 'lib/strings/handles' -import {s} from 'lib/styles' -import {atoms as a} from '#/alf' -import {RichText as RichTextCom} from '#/components/RichText' -import {Link} from '../util/Link' -import {Text} from '../util/text/Text' -import {UserAvatar} from '../util/UserAvatar' - -export const ListCard = ({ - testID, - list, - noBg, - noBorder, - renderButton, - style, -}: { - testID?: string - list: AppBskyGraphDefs.ListView - noBg?: boolean - noBorder?: boolean - renderButton?: () => JSX.Element - style?: StyleProp -}) => { - const pal = usePalette('default') - const {currentAccount} = useSession() - - const rkey = React.useMemo(() => { - try { - const urip = new AtUri(list.uri) - return urip.rkey - } catch { - return '' - } - }, [list]) - - const descriptionRichText = React.useMemo(() => { - if (list.description) { - return new RichText({ - text: list.description, - facets: list.descriptionFacets, - }) - } - return undefined - }, [list]) - - return ( - - - - - - - - {sanitizeDisplayName(list.name)} - - - {list.purpose === 'app.bsky.graph.defs#curatelist' && - (list.creator.did === currentAccount?.did ? ( - User list by you - ) : ( - - User list by {sanitizeHandle(list.creator.handle, '@')} - - ))} - {list.purpose === 'app.bsky.graph.defs#modlist' && - (list.creator.did === currentAccount?.did ? ( - Moderation list by you - ) : ( - - Moderation list by {sanitizeHandle(list.creator.handle, '@')} - - ))} - - - {list.viewer?.muted ? ( - - - Muted - - - ) : null} - - {list.viewer?.blocked ? ( - - - Blocked - - - ) : null} - - - {renderButton ? ( - {renderButton()} - ) : undefined} - - {descriptionRichText ? ( - - - - ) : undefined} - - ) -} - -const styles = StyleSheet.create({ - outer: { - borderTopWidth: StyleSheet.hairlineWidth, - paddingHorizontal: 6, - }, - outerNoBorder: { - borderTopWidth: 0, - }, - layout: { - flexDirection: 'row', - alignItems: 'center', - }, - layoutAvi: { - width: 54, - paddingLeft: 4, - paddingTop: 8, - paddingBottom: 10, - }, - avi: { - width: 40, - height: 40, - borderRadius: 20, - resizeMode: 'cover', - }, - layoutContent: { - flex: 1, - paddingRight: 10, - paddingTop: 10, - paddingBottom: 10, - }, - layoutButton: { - paddingRight: 10, - }, - details: { - paddingLeft: 54, - paddingRight: 10, - paddingBottom: 10, - }, - pill: { - borderRadius: 4, - paddingHorizontal: 6, - paddingVertical: 2, - }, - btn: { - paddingVertical: 7, - borderRadius: 50, - marginLeft: 6, - paddingHorizontal: 14, - }, -}) diff --git a/src/view/com/lists/MyLists.tsx b/src/view/com/lists/MyLists.tsx index 472d2688c7..b56fa6c75f 100644 --- a/src/view/com/lists/MyLists.tsx +++ b/src/view/com/lists/MyLists.tsx @@ -4,7 +4,6 @@ import { FlatList as RNFlatList, RefreshControl, StyleProp, - StyleSheet, View, ViewStyle, } from 'react-native' @@ -18,10 +17,13 @@ import {MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists' import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' +import {isWeb} from 'platform/detection' +import {useModerationOpts} from 'state/preferences/moderation-opts' import {EmptyState} from 'view/com/util/EmptyState' +import {atoms as a, useTheme} from '#/alf' +import * as ListCard from '#/components/ListCard' import {ErrorMessage} from '../util/error/ErrorMessage' import {List} from '../util/List' -import {ListCard} from './ListCard' const LOADING = {_reactKey: '__loading__'} const EMPTY = {_reactKey: '__empty__'} @@ -41,8 +43,10 @@ export function MyLists({ testID?: string }) { const pal = usePalette('default') + const t = useTheme() const {track} = useAnalytics() const {_} = useLingui() + const moderationOpts = useModerationOpts() const [isPTRing, setIsPTRing] = React.useState(false) const {data, isFetching, isFetched, isError, error, refetch} = useMyListsQuery(filter) @@ -53,7 +57,7 @@ export function MyLists({ if (isError && isEmpty) { items = items.concat([ERROR_ITEM]) } - if (!isFetched && isFetching) { + if ((!isFetched && isFetching) || !moderationOpts) { items = items.concat([LOADING]) } else if (isEmpty) { items = items.concat([EMPTY]) @@ -61,7 +65,7 @@ export function MyLists({ items = items.concat(data) } return items - }, [isError, isEmpty, isFetched, isFetching, data]) + }, [isError, isEmpty, isFetched, isFetching, moderationOpts, data]) // events // = @@ -85,7 +89,6 @@ export function MyLists({ if (item === EMPTY) { return ( ) } else if (item === LOADING) { return ( - + ) @@ -109,15 +111,18 @@ export function MyLists({ return renderItem ? ( renderItem(item, index) ) : ( - + + + ) }, - [error, onRefresh, renderItem, _], + [renderItem, t.atoms.border_contrast_low, _, error, onRefresh], ) if (inline) { @@ -166,10 +171,3 @@ export function MyLists({ ) } } - -const styles = StyleSheet.create({ - item: { - paddingHorizontal: 18, - paddingVertical: 4, - }, -}) diff --git a/src/view/com/util/post-embeds/ListEmbed.tsx b/src/view/com/util/post-embeds/ListEmbed.tsx deleted file mode 100644 index fc5ad270fc..0000000000 --- a/src/view/com/util/post-embeds/ListEmbed.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' -import {usePalette} from 'lib/hooks/usePalette' -import {ListCard} from 'view/com/lists/ListCard' -import {AppBskyGraphDefs} from '@atproto/api' -import {s} from 'lib/styles' - -export function ListEmbed({ - item, - style, -}: { - item: AppBskyGraphDefs.ListView - style?: StyleProp -}) { - const pal = usePalette('default') - - return ( - - - - ) -} - -const styles = StyleSheet.create({ - container: { - borderRadius: 8, - }, - card: { - borderTopWidth: 0, - borderRadius: 8, - }, -}) diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index 0462212fbd..9c13644834 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -25,13 +25,13 @@ import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {usePalette} from 'lib/hooks/usePalette' import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' -import {atoms as a} from '#/alf' +import {atoms as a, useTheme} from '#/alf' +import * as ListCard from '#/components/ListCard' import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard' import {ContentHider} from '../../../../components/moderation/ContentHider' import {AutoSizedImage} from '../images/AutoSizedImage' import {ImageLayoutGrid} from '../images/ImageLayoutGrid' import {ExternalLinkEmbed} from './ExternalLinkEmbed' -import {ListEmbed} from './ListEmbed' import {MaybeQuoteEmbed} from './QuoteEmbed' type Embed = @@ -203,10 +203,20 @@ function MaybeListCard({view}: {view: AppBskyGraphDefs.ListView}) { const moderation = React.useMemo(() => { return moderationOpts ? moderateUserList(view, moderationOpts) : undefined }, [view, moderationOpts]) + const t = useTheme() return ( - + + + ) } diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index c0489c9fd9..0c2c6405fc 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -142,6 +142,7 @@ function ProfileListScreenLoaded({ const queryClient = useQueryClient() const {openComposer} = useComposerControls() const setMinimalShellMode = useSetMinimalShellMode() + const {currentAccount} = useSession() const {rkey} = route.params const feedSectionRef = React.useRef(null) const aboutSectionRef = React.useRef(null) @@ -149,6 +150,7 @@ function ProfileListScreenLoaded({ const isCurateList = list.purpose === AppBskyGraphDefs.CURATELIST const isScreenFocused = useIsFocused() const isHidden = list.labels?.findIndex(l => l.val === '!hide') !== -1 + const isOwner = currentAccount?.did === list.creator.did const moderation = React.useMemo(() => { return moderateUserList(list, moderationOpts) @@ -191,7 +193,7 @@ function ProfileListScreenLoaded({ if (isCurateList) { return ( - + @@ -241,7 +243,7 @@ function ProfileListScreenLoaded({ ) } return ( - +