Fix labeler header scroll and loading/error states (#8088)
* add forwardRef to Layout.Content * lift scrollview up out of inner component * fix scrolling on android (#8188)
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
import React, {useContext, useMemo} from 'react'
|
import {forwardRef, memo, useContext, useMemo} from 'react'
|
||||||
import {StyleSheet, View, ViewProps, ViewStyle} from 'react-native'
|
import {StyleSheet, View, type ViewProps, type ViewStyle} from 'react-native'
|
||||||
import {StyleProp} from 'react-native'
|
import {type StyleProp} from 'react-native'
|
||||||
import {
|
import {
|
||||||
KeyboardAwareScrollView,
|
KeyboardAwareScrollView,
|
||||||
KeyboardAwareScrollViewProps,
|
type KeyboardAwareScrollViewProps,
|
||||||
} from 'react-native-keyboard-controller'
|
} from 'react-native-keyboard-controller'
|
||||||
import Animated, {
|
import Animated, {
|
||||||
AnimatedScrollViewProps,
|
type AnimatedScrollViewProps,
|
||||||
useAnimatedProps,
|
useAnimatedProps,
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
@@ -35,7 +35,7 @@ export type ScreenProps = React.ComponentProps<typeof View> & {
|
|||||||
/**
|
/**
|
||||||
* Outermost component of every screen
|
* Outermost component of every screen
|
||||||
*/
|
*/
|
||||||
export const Screen = React.memo(function Screen({
|
export const Screen = memo(function Screen({
|
||||||
style,
|
style,
|
||||||
noInsetTop,
|
noInsetTop,
|
||||||
...props
|
...props
|
||||||
@@ -61,49 +61,55 @@ export type ContentProps = AnimatedScrollViewProps & {
|
|||||||
/**
|
/**
|
||||||
* Default scroll view for simple pages
|
* Default scroll view for simple pages
|
||||||
*/
|
*/
|
||||||
export const Content = React.memo(function Content({
|
export const Content = memo(
|
||||||
children,
|
forwardRef<Animated.ScrollView, ContentProps>(function Content(
|
||||||
style,
|
{
|
||||||
contentContainerStyle,
|
children,
|
||||||
ignoreTabletLayoutOffset,
|
style,
|
||||||
...props
|
contentContainerStyle,
|
||||||
}: ContentProps) {
|
ignoreTabletLayoutOffset,
|
||||||
const t = useTheme()
|
...props
|
||||||
const {footerHeight} = useShellLayout()
|
},
|
||||||
const animatedProps = useAnimatedProps(() => {
|
ref,
|
||||||
return {
|
) {
|
||||||
scrollIndicatorInsets: {
|
const t = useTheme()
|
||||||
bottom: footerHeight.get(),
|
const {footerHeight} = useShellLayout()
|
||||||
top: 0,
|
const animatedProps = useAnimatedProps(() => {
|
||||||
right: 1,
|
return {
|
||||||
},
|
scrollIndicatorInsets: {
|
||||||
} satisfies AnimatedScrollViewProps
|
bottom: footerHeight.get(),
|
||||||
})
|
top: 0,
|
||||||
|
right: 1,
|
||||||
|
},
|
||||||
|
} satisfies AnimatedScrollViewProps
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.ScrollView
|
<Animated.ScrollView
|
||||||
id="content"
|
ref={ref}
|
||||||
automaticallyAdjustsScrollIndicatorInsets={false}
|
id="content"
|
||||||
indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'}
|
automaticallyAdjustsScrollIndicatorInsets={false}
|
||||||
// sets the scroll inset to the height of the footer
|
indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'}
|
||||||
animatedProps={animatedProps}
|
// sets the scroll inset to the height of the footer
|
||||||
style={[scrollViewStyles.common, style]}
|
animatedProps={animatedProps}
|
||||||
contentContainerStyle={[
|
style={[scrollViewStyles.common, style]}
|
||||||
scrollViewStyles.contentContainer,
|
contentContainerStyle={[
|
||||||
contentContainerStyle,
|
scrollViewStyles.contentContainer,
|
||||||
]}
|
contentContainerStyle,
|
||||||
{...props}>
|
]}
|
||||||
{isWeb ? (
|
{...props}>
|
||||||
<Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
|
{isWeb ? (
|
||||||
{/* @ts-expect-error web only -esb */}
|
<Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
|
||||||
{children}
|
{/* @ts-expect-error web only -esb */}
|
||||||
</Center>
|
{children}
|
||||||
) : (
|
</Center>
|
||||||
children
|
) : (
|
||||||
)}
|
children
|
||||||
</Animated.ScrollView>
|
)}
|
||||||
)
|
</Animated.ScrollView>
|
||||||
})
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const scrollViewStyles = StyleSheet.create({
|
const scrollViewStyles = StyleSheet.create({
|
||||||
common: {
|
common: {
|
||||||
@@ -124,7 +130,7 @@ export type KeyboardAwareContentProps = KeyboardAwareScrollViewProps & {
|
|||||||
*
|
*
|
||||||
* BE SURE TO TEST THIS WHEN USING, it's untested as of writing this comment.
|
* BE SURE TO TEST THIS WHEN USING, it's untested as of writing this comment.
|
||||||
*/
|
*/
|
||||||
export const KeyboardAwareContent = React.memo(function LayoutScrollView({
|
export const KeyboardAwareContent = memo(function LayoutKeyboardAwareContent({
|
||||||
children,
|
children,
|
||||||
style,
|
style,
|
||||||
contentContainerStyle,
|
contentContainerStyle,
|
||||||
@@ -147,7 +153,7 @@ export const KeyboardAwareContent = React.memo(function LayoutScrollView({
|
|||||||
/**
|
/**
|
||||||
* Utility component to center content within the screen
|
* Utility component to center content within the screen
|
||||||
*/
|
*/
|
||||||
export const Center = React.memo(function LayoutContent({
|
export const Center = memo(function LayoutCenter({
|
||||||
children,
|
children,
|
||||||
style,
|
style,
|
||||||
ignoreTabletLayoutOffset,
|
ignoreTabletLayoutOffset,
|
||||||
@@ -192,7 +198,7 @@ export const Center = React.memo(function LayoutContent({
|
|||||||
/**
|
/**
|
||||||
* Only used within `Layout.Screen`, not for reuse
|
* Only used within `Layout.Screen`, not for reuse
|
||||||
*/
|
*/
|
||||||
const WebCenterBorders = React.memo(function LayoutContent() {
|
const WebCenterBorders = memo(function LayoutWebCenterBorders() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const {centerColumnOffset} = useLayoutBreakpoints()
|
const {centerColumnOffset} = useLayoutBreakpoints()
|
||||||
|
|||||||
@@ -7,16 +7,16 @@ import {useQueryClient} from '@tanstack/react-query'
|
|||||||
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {FeedDescriptor} from '#/state/queries/post-feed'
|
import {type FeedDescriptor} from '#/state/queries/post-feed'
|
||||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||||
import {PostFeed} from '#/view/com/posts/PostFeed'
|
import {PostFeed} from '#/view/com/posts/PostFeed'
|
||||||
import {EmptyState} from '#/view/com/util/EmptyState'
|
import {EmptyState} from '#/view/com/util/EmptyState'
|
||||||
import {ListRef} from '#/view/com/util/List'
|
import {type ListRef} from '#/view/com/util/List'
|
||||||
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
|
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
|
||||||
import {Text} from '#/view/com/util/text/Text'
|
import {Text} from '#/view/com/util/text/Text'
|
||||||
import {ios} from '#/alf'
|
import {ios} from '#/alf'
|
||||||
import {SectionRef} from './types'
|
import {type SectionRef} from './types'
|
||||||
|
|
||||||
interface FeedSectionProps {
|
interface FeedSectionProps {
|
||||||
feed: FeedDescriptor
|
feed: FeedDescriptor
|
||||||
@@ -58,6 +58,7 @@ export const ProfileFeedSection = React.forwardRef<
|
|||||||
truncateAndInvalidate(queryClient, FEED_RQKEY(feed))
|
truncateAndInvalidate(queryClient, FEED_RQKEY(feed))
|
||||||
setHasNew(false)
|
setHasNew(false)
|
||||||
}, [scrollElRef, headerHeight, queryClient, feed, setHasNew])
|
}, [scrollElRef, headerHeight, queryClient, feed, setHasNew])
|
||||||
|
|
||||||
React.useImperativeHandle(ref, () => ({
|
React.useImperativeHandle(ref, () => ({
|
||||||
scrollToTop: onScrollToTop,
|
scrollToTop: onScrollToTop,
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {findNodeHandle, View} from 'react-native'
|
import {findNodeHandle, View} from 'react-native'
|
||||||
|
import type Animated from 'react-native-reanimated'
|
||||||
import {useSafeAreaFrame} from 'react-native-safe-area-context'
|
import {useSafeAreaFrame} from 'react-native-safe-area-context'
|
||||||
import {
|
import {
|
||||||
AppBskyLabelerDefs,
|
type AppBskyLabelerDefs,
|
||||||
InterpretedLabelValueDefinition,
|
type InterpretedLabelValueDefinition,
|
||||||
interpretLabelValueDefinitions,
|
interpretLabelValueDefinitions,
|
||||||
ModerationOpts,
|
type ModerationOpts,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
@@ -14,7 +15,7 @@ import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIX
|
|||||||
import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
|
import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
|
||||||
import {useScrollHandlers} from '#/lib/ScrollContext'
|
import {useScrollHandlers} from '#/lib/ScrollContext'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {ListRef} from '#/view/com/util/List'
|
import {type ListRef} from '#/view/com/util/List'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Divider} from '#/components/Divider'
|
import {Divider} from '#/components/Divider'
|
||||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||||
@@ -23,7 +24,7 @@ import {Loader} from '#/components/Loader'
|
|||||||
import {LabelerLabelPreference} from '#/components/moderation/LabelPreference'
|
import {LabelerLabelPreference} from '#/components/moderation/LabelPreference'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {ErrorState} from '../ErrorState'
|
import {ErrorState} from '../ErrorState'
|
||||||
import {SectionRef} from './types'
|
import {type SectionRef} from './types'
|
||||||
|
|
||||||
interface LabelsSectionProps {
|
interface LabelsSectionProps {
|
||||||
isLabelerLoading: boolean
|
isLabelerLoading: boolean
|
||||||
@@ -54,64 +55,6 @@ export const ProfileLabelsSection = React.forwardRef<
|
|||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {height: minHeight} = useSafeAreaFrame()
|
const {height: minHeight} = useSafeAreaFrame()
|
||||||
|
|
||||||
const onScrollToTop = React.useCallback(() => {
|
|
||||||
// @ts-ignore TODO fix this
|
|
||||||
scrollElRef.current?.scrollTo({
|
|
||||||
animated: isNative,
|
|
||||||
x: 0,
|
|
||||||
y: -headerHeight,
|
|
||||||
})
|
|
||||||
}, [scrollElRef, headerHeight])
|
|
||||||
|
|
||||||
React.useImperativeHandle(ref, () => ({
|
|
||||||
scrollToTop: onScrollToTop,
|
|
||||||
}))
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (isFocused && scrollElRef.current) {
|
|
||||||
const nativeTag = findNodeHandle(scrollElRef.current)
|
|
||||||
setScrollViewTag(nativeTag)
|
|
||||||
}
|
|
||||||
}, [isFocused, scrollElRef, setScrollViewTag])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout.Center style={{flex: 1, minHeight}}>
|
|
||||||
{isLabelerLoading ? (
|
|
||||||
<View style={[a.w_full, a.align_center]}>
|
|
||||||
<Loader size="xl" />
|
|
||||||
</View>
|
|
||||||
) : labelerError || !labelerInfo ? (
|
|
||||||
<ErrorState
|
|
||||||
error={
|
|
||||||
labelerError?.toString() ||
|
|
||||||
_(msg`Something went wrong, please try again.`)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ProfileLabelsSectionInner
|
|
||||||
moderationOpts={moderationOpts}
|
|
||||||
labelerInfo={labelerInfo}
|
|
||||||
scrollElRef={scrollElRef}
|
|
||||||
headerHeight={headerHeight}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Layout.Center>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export function ProfileLabelsSectionInner({
|
|
||||||
moderationOpts,
|
|
||||||
labelerInfo,
|
|
||||||
scrollElRef,
|
|
||||||
headerHeight,
|
|
||||||
}: {
|
|
||||||
moderationOpts: ModerationOpts
|
|
||||||
labelerInfo: AppBskyLabelerDefs.LabelerViewDetailed
|
|
||||||
scrollElRef: ListRef
|
|
||||||
headerHeight: number
|
|
||||||
}) {
|
|
||||||
const t = useTheme()
|
|
||||||
|
|
||||||
// Intentionally destructured outside the main thread closure.
|
// Intentionally destructured outside the main thread closure.
|
||||||
// See https://github.com/bluesky-social/social-app/pull/4108.
|
// See https://github.com/bluesky-social/social-app/pull/4108.
|
||||||
const {
|
const {
|
||||||
@@ -135,6 +78,70 @@ export function ProfileLabelsSectionInner({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const onScrollToTop = React.useCallback(() => {
|
||||||
|
// @ts-ignore TODO fix this
|
||||||
|
scrollElRef.current?.scrollTo({
|
||||||
|
animated: isNative,
|
||||||
|
x: 0,
|
||||||
|
y: -headerHeight,
|
||||||
|
})
|
||||||
|
}, [scrollElRef, headerHeight])
|
||||||
|
|
||||||
|
React.useImperativeHandle(ref, () => ({
|
||||||
|
scrollToTop: onScrollToTop,
|
||||||
|
}))
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (isFocused && scrollElRef.current) {
|
||||||
|
const nativeTag = findNodeHandle(scrollElRef.current)
|
||||||
|
setScrollViewTag(nativeTag)
|
||||||
|
}
|
||||||
|
}, [isFocused, scrollElRef, setScrollViewTag])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.Center style={{minHeight}}>
|
||||||
|
<Layout.Content
|
||||||
|
ref={scrollElRef as React.Ref<Animated.ScrollView>}
|
||||||
|
scrollEventThrottle={1}
|
||||||
|
contentContainerStyle={{
|
||||||
|
paddingTop: headerHeight,
|
||||||
|
borderWidth: 0,
|
||||||
|
}}
|
||||||
|
contentOffset={{x: 0, y: headerHeight * -1}}
|
||||||
|
onScroll={scrollHandler}>
|
||||||
|
{isLabelerLoading ? (
|
||||||
|
<View style={[a.w_full, a.align_center, a.py_4xl]}>
|
||||||
|
<Loader size="xl" />
|
||||||
|
</View>
|
||||||
|
) : labelerError || !labelerInfo ? (
|
||||||
|
<View style={[a.w_full, a.align_center, a.py_4xl]}>
|
||||||
|
<ErrorState
|
||||||
|
error={
|
||||||
|
labelerError?.toString() ||
|
||||||
|
_(msg`Something went wrong, please try again.`)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<ProfileLabelsSectionInner
|
||||||
|
moderationOpts={moderationOpts}
|
||||||
|
labelerInfo={labelerInfo}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Layout.Content>
|
||||||
|
</Layout.Center>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export function ProfileLabelsSectionInner({
|
||||||
|
moderationOpts,
|
||||||
|
labelerInfo,
|
||||||
|
}: {
|
||||||
|
moderationOpts: ModerationOpts
|
||||||
|
labelerInfo: AppBskyLabelerDefs.LabelerViewDetailed
|
||||||
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
const {labelValues} = labelerInfo.policies
|
const {labelValues} = labelerInfo.policies
|
||||||
const isSubscribed = isLabelerSubscribed(labelerInfo, moderationOpts)
|
const isSubscribed = isLabelerSubscribed(labelerInfo, moderationOpts)
|
||||||
const labelDefs = React.useMemo(() => {
|
const labelDefs = React.useMemo(() => {
|
||||||
@@ -147,88 +154,76 @@ export function ProfileLabelsSectionInner({
|
|||||||
}, [labelerInfo, labelValues])
|
}, [labelerInfo, labelValues])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Content
|
<View style={[a.pt_xl, a.px_lg, a.border_t, t.atoms.border_contrast_low]}>
|
||||||
// @ts-expect-error TODO fix this
|
<View>
|
||||||
ref={scrollElRef}
|
<Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_sm]}>
|
||||||
scrollEventThrottle={1}
|
<Trans>
|
||||||
contentContainerStyle={{
|
Labels are annotations on users and content. They can be used to
|
||||||
paddingTop: headerHeight,
|
hide, warn, and categorize the network.
|
||||||
borderWidth: 0,
|
</Trans>
|
||||||
}}
|
</Text>
|
||||||
contentOffset={{x: 0, y: headerHeight * -1}}
|
{labelerInfo.creator.viewer?.blocking ? (
|
||||||
onScroll={scrollHandler}>
|
<View style={[a.flex_row, a.gap_sm, a.align_center, a.mt_md]}>
|
||||||
<View style={[a.pt_xl, a.px_lg, a.border_t, t.atoms.border_contrast_low]}>
|
<CircleInfo size="sm" fill={t.atoms.text_contrast_medium.color} />
|
||||||
<View>
|
<Text
|
||||||
<Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_sm]}>
|
style={[t.atoms.text_contrast_high, a.leading_snug, a.text_sm]}>
|
||||||
|
<Trans>
|
||||||
|
Blocking does not prevent this labeler from placing labels on
|
||||||
|
your account.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
{labelValues.length === 0 ? (
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.pt_xl,
|
||||||
|
t.atoms.text_contrast_high,
|
||||||
|
a.leading_snug,
|
||||||
|
a.text_sm,
|
||||||
|
]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
Labels are annotations on users and content. They can be used to
|
This labeler hasn't declared what labels it publishes, and may not
|
||||||
hide, warn, and categorize the network.
|
be active.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
{labelerInfo.creator.viewer?.blocking ? (
|
) : !isSubscribed ? (
|
||||||
<View style={[a.flex_row, a.gap_sm, a.align_center, a.mt_md]}>
|
<Text
|
||||||
<CircleInfo size="sm" fill={t.atoms.text_contrast_medium.color} />
|
|
||||||
<Text
|
|
||||||
style={[t.atoms.text_contrast_high, a.leading_snug, a.text_sm]}>
|
|
||||||
<Trans>
|
|
||||||
Blocking does not prevent this labeler from placing labels on
|
|
||||||
your account.
|
|
||||||
</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
) : null}
|
|
||||||
{labelValues.length === 0 ? (
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
a.pt_xl,
|
|
||||||
t.atoms.text_contrast_high,
|
|
||||||
a.leading_snug,
|
|
||||||
a.text_sm,
|
|
||||||
]}>
|
|
||||||
<Trans>
|
|
||||||
This labeler hasn't declared what labels it publishes, and may
|
|
||||||
not be active.
|
|
||||||
</Trans>
|
|
||||||
</Text>
|
|
||||||
) : !isSubscribed ? (
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
a.pt_xl,
|
|
||||||
t.atoms.text_contrast_high,
|
|
||||||
a.leading_snug,
|
|
||||||
a.text_sm,
|
|
||||||
]}>
|
|
||||||
<Trans>
|
|
||||||
Subscribe to @{labelerInfo.creator.handle} to use these labels:
|
|
||||||
</Trans>
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
</View>
|
|
||||||
{labelDefs.length > 0 && (
|
|
||||||
<View
|
|
||||||
style={[
|
style={[
|
||||||
a.mt_xl,
|
a.pt_xl,
|
||||||
a.w_full,
|
t.atoms.text_contrast_high,
|
||||||
a.rounded_md,
|
a.leading_snug,
|
||||||
a.overflow_hidden,
|
a.text_sm,
|
||||||
t.atoms.bg_contrast_25,
|
|
||||||
]}>
|
]}>
|
||||||
{labelDefs.map((labelDef, i) => {
|
<Trans>
|
||||||
return (
|
Subscribe to @{labelerInfo.creator.handle} to use these labels:
|
||||||
<React.Fragment key={labelDef.identifier}>
|
</Trans>
|
||||||
{i !== 0 && <Divider />}
|
</Text>
|
||||||
<LabelerLabelPreference
|
) : null}
|
||||||
disabled={isSubscribed ? undefined : true}
|
|
||||||
labelDefinition={labelDef}
|
|
||||||
labelerDid={labelerInfo.creator.did}
|
|
||||||
/>
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
<View style={{height: 100}} />
|
|
||||||
</View>
|
</View>
|
||||||
</Layout.Content>
|
{labelDefs.length > 0 && (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.mt_xl,
|
||||||
|
a.w_full,
|
||||||
|
a.rounded_md,
|
||||||
|
a.overflow_hidden,
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
]}>
|
||||||
|
{labelDefs.map((labelDef, i) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={labelDef.identifier}>
|
||||||
|
{i !== 0 && <Divider />}
|
||||||
|
<LabelerLabelPreference
|
||||||
|
disabled={isSubscribed ? undefined : true}
|
||||||
|
labelDefinition={labelDef}
|
||||||
|
labelerDid={labelerInfo.creator.did}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user