about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/Layout/index.tsx108
-rw-r--r--src/screens/Profile/Sections/Feed.tsx7
-rw-r--r--src/screens/Profile/Sections/Labels.tsx251
3 files changed, 184 insertions, 182 deletions
diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx
index a61192b86..5891ca863 100644
--- a/src/components/Layout/index.tsx
+++ b/src/components/Layout/index.tsx
@@ -1,12 +1,12 @@
-import React, {useContext, useMemo} from 'react'
-import {StyleSheet, View, ViewProps, ViewStyle} from 'react-native'
-import {StyleProp} from 'react-native'
+import {forwardRef, memo, useContext, useMemo} from 'react'
+import {StyleSheet, View, type ViewProps, type ViewStyle} from 'react-native'
+import {type StyleProp} from 'react-native'
 import {
   KeyboardAwareScrollView,
-  KeyboardAwareScrollViewProps,
+  type KeyboardAwareScrollViewProps,
 } from 'react-native-keyboard-controller'
 import Animated, {
-  AnimatedScrollViewProps,
+  type AnimatedScrollViewProps,
   useAnimatedProps,
 } from 'react-native-reanimated'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
@@ -35,7 +35,7 @@ export type ScreenProps = React.ComponentProps<typeof View> & {
 /**
  * Outermost component of every screen
  */
-export const Screen = React.memo(function Screen({
+export const Screen = memo(function Screen({
   style,
   noInsetTop,
   ...props
@@ -61,49 +61,55 @@ export type ContentProps = AnimatedScrollViewProps & {
 /**
  * Default scroll view for simple pages
  */
-export const Content = React.memo(function Content({
-  children,
-  style,
-  contentContainerStyle,
-  ignoreTabletLayoutOffset,
-  ...props
-}: ContentProps) {
-  const t = useTheme()
-  const {footerHeight} = useShellLayout()
-  const animatedProps = useAnimatedProps(() => {
-    return {
-      scrollIndicatorInsets: {
-        bottom: footerHeight.get(),
-        top: 0,
-        right: 1,
-      },
-    } satisfies AnimatedScrollViewProps
-  })
+export const Content = memo(
+  forwardRef<Animated.ScrollView, ContentProps>(function Content(
+    {
+      children,
+      style,
+      contentContainerStyle,
+      ignoreTabletLayoutOffset,
+      ...props
+    },
+    ref,
+  ) {
+    const t = useTheme()
+    const {footerHeight} = useShellLayout()
+    const animatedProps = useAnimatedProps(() => {
+      return {
+        scrollIndicatorInsets: {
+          bottom: footerHeight.get(),
+          top: 0,
+          right: 1,
+        },
+      } satisfies AnimatedScrollViewProps
+    })
 
-  return (
-    <Animated.ScrollView
-      id="content"
-      automaticallyAdjustsScrollIndicatorInsets={false}
-      indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'}
-      // sets the scroll inset to the height of the footer
-      animatedProps={animatedProps}
-      style={[scrollViewStyles.common, style]}
-      contentContainerStyle={[
-        scrollViewStyles.contentContainer,
-        contentContainerStyle,
-      ]}
-      {...props}>
-      {isWeb ? (
-        <Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
-          {/* @ts-expect-error web only -esb */}
-          {children}
-        </Center>
-      ) : (
-        children
-      )}
-    </Animated.ScrollView>
-  )
-})
+    return (
+      <Animated.ScrollView
+        ref={ref}
+        id="content"
+        automaticallyAdjustsScrollIndicatorInsets={false}
+        indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'}
+        // sets the scroll inset to the height of the footer
+        animatedProps={animatedProps}
+        style={[scrollViewStyles.common, style]}
+        contentContainerStyle={[
+          scrollViewStyles.contentContainer,
+          contentContainerStyle,
+        ]}
+        {...props}>
+        {isWeb ? (
+          <Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
+            {/* @ts-expect-error web only -esb */}
+            {children}
+          </Center>
+        ) : (
+          children
+        )}
+      </Animated.ScrollView>
+    )
+  }),
+)
 
 const scrollViewStyles = StyleSheet.create({
   common: {
@@ -124,7 +130,7 @@ export type KeyboardAwareContentProps = KeyboardAwareScrollViewProps & {
  *
  * 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,
   style,
   contentContainerStyle,
@@ -147,7 +153,7 @@ export const KeyboardAwareContent = React.memo(function LayoutScrollView({
 /**
  * Utility component to center content within the screen
  */
-export const Center = React.memo(function LayoutContent({
+export const Center = memo(function LayoutCenter({
   children,
   style,
   ignoreTabletLayoutOffset,
@@ -192,7 +198,7 @@ export const Center = React.memo(function LayoutContent({
 /**
  * Only used within `Layout.Screen`, not for reuse
  */
-const WebCenterBorders = React.memo(function LayoutContent() {
+const WebCenterBorders = memo(function LayoutWebCenterBorders() {
   const t = useTheme()
   const {gtMobile} = useBreakpoints()
   const {centerColumnOffset} = useLayoutBreakpoints()
diff --git a/src/screens/Profile/Sections/Feed.tsx b/src/screens/Profile/Sections/Feed.tsx
index 3e3fe973e..0691fd729 100644
--- a/src/screens/Profile/Sections/Feed.tsx
+++ b/src/screens/Profile/Sections/Feed.tsx
@@ -7,16 +7,16 @@ import {useQueryClient} from '@tanstack/react-query'
 import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
 import {usePalette} from '#/lib/hooks/usePalette'
 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 {truncateAndInvalidate} from '#/state/queries/util'
 import {PostFeed} from '#/view/com/posts/PostFeed'
 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 {Text} from '#/view/com/util/text/Text'
 import {ios} from '#/alf'
-import {SectionRef} from './types'
+import {type SectionRef} from './types'
 
 interface FeedSectionProps {
   feed: FeedDescriptor
@@ -58,6 +58,7 @@ export const ProfileFeedSection = React.forwardRef<
     truncateAndInvalidate(queryClient, FEED_RQKEY(feed))
     setHasNew(false)
   }, [scrollElRef, headerHeight, queryClient, feed, setHasNew])
+
   React.useImperativeHandle(ref, () => ({
     scrollToTop: onScrollToTop,
   }))
diff --git a/src/screens/Profile/Sections/Labels.tsx b/src/screens/Profile/Sections/Labels.tsx
index 770464a71..b7f702f11 100644
--- a/src/screens/Profile/Sections/Labels.tsx
+++ b/src/screens/Profile/Sections/Labels.tsx
@@ -1,11 +1,12 @@
 import React from 'react'
 import {findNodeHandle, View} from 'react-native'
+import type Animated from 'react-native-reanimated'
 import {useSafeAreaFrame} from 'react-native-safe-area-context'
 import {
-  AppBskyLabelerDefs,
-  InterpretedLabelValueDefinition,
+  type AppBskyLabelerDefs,
+  type InterpretedLabelValueDefinition,
   interpretLabelValueDefinitions,
-  ModerationOpts,
+  type ModerationOpts,
 } from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
@@ -14,7 +15,7 @@ import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIX
 import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
 import {useScrollHandlers} from '#/lib/ScrollContext'
 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 {Divider} from '#/components/Divider'
 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 {Text} from '#/components/Typography'
 import {ErrorState} from '../ErrorState'
-import {SectionRef} from './types'
+import {type SectionRef} from './types'
 
 interface LabelsSectionProps {
   isLabelerLoading: boolean
@@ -54,6 +55,29 @@ export const ProfileLabelsSection = React.forwardRef<
   const {_} = useLingui()
   const {height: minHeight} = useSafeAreaFrame()
 
+  // Intentionally destructured outside the main thread closure.
+  // See https://github.com/bluesky-social/social-app/pull/4108.
+  const {
+    onBeginDrag: onBeginDragFromContext,
+    onEndDrag: onEndDragFromContext,
+    onScroll: onScrollFromContext,
+    onMomentumEnd: onMomentumEndFromContext,
+  } = useScrollHandlers()
+  const scrollHandler = useAnimatedScrollHandler({
+    onBeginDrag(e, ctx) {
+      onBeginDragFromContext?.(e, ctx)
+    },
+    onEndDrag(e, ctx) {
+      onEndDragFromContext?.(e, ctx)
+    },
+    onScroll(e, ctx) {
+      onScrollFromContext?.(e, ctx)
+    },
+    onMomentumEnd(e, ctx) {
+      onMomentumEndFromContext?.(e, ctx)
+    },
+  })
+
   const onScrollToTop = React.useCallback(() => {
     // @ts-ignore TODO fix this
     scrollElRef.current?.scrollTo({
@@ -75,26 +99,36 @@ export const ProfileLabelsSection = React.forwardRef<
   }, [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 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>
   )
 })
@@ -102,39 +136,12 @@ export const ProfileLabelsSection = React.forwardRef<
 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.
-  // See https://github.com/bluesky-social/social-app/pull/4108.
-  const {
-    onBeginDrag: onBeginDragFromContext,
-    onEndDrag: onEndDragFromContext,
-    onScroll: onScrollFromContext,
-    onMomentumEnd: onMomentumEndFromContext,
-  } = useScrollHandlers()
-  const scrollHandler = useAnimatedScrollHandler({
-    onBeginDrag(e, ctx) {
-      onBeginDragFromContext?.(e, ctx)
-    },
-    onEndDrag(e, ctx) {
-      onEndDragFromContext?.(e, ctx)
-    },
-    onScroll(e, ctx) {
-      onScrollFromContext?.(e, ctx)
-    },
-    onMomentumEnd(e, ctx) {
-      onMomentumEndFromContext?.(e, ctx)
-    },
-  })
-
   const {labelValues} = labelerInfo.policies
   const isSubscribed = isLabelerSubscribed(labelerInfo, moderationOpts)
   const labelDefs = React.useMemo(() => {
@@ -147,88 +154,76 @@ export function ProfileLabelsSectionInner({
   }, [labelerInfo, labelValues])
 
   return (
-    <Layout.Content
-      // @ts-expect-error TODO fix this
-      ref={scrollElRef}
-      scrollEventThrottle={1}
-      contentContainerStyle={{
-        paddingTop: headerHeight,
-        borderWidth: 0,
-      }}
-      contentOffset={{x: 0, y: headerHeight * -1}}
-      onScroll={scrollHandler}>
-      <View style={[a.pt_xl, a.px_lg, a.border_t, t.atoms.border_contrast_low]}>
-        <View>
-          <Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_sm]}>
-            <Trans>
-              Labels are annotations on users and content. They can be used to
-              hide, warn, and categorize the network.
-            </Trans>
-          </Text>
-          {labelerInfo.creator.viewer?.blocking ? (
-            <View style={[a.flex_row, a.gap_sm, a.align_center, a.mt_md]}>
-              <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 ? (
+    <View style={[a.pt_xl, a.px_lg, a.border_t, t.atoms.border_contrast_low]}>
+      <View>
+        <Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_sm]}>
+          <Trans>
+            Labels are annotations on users and content. They can be used to
+            hide, warn, and categorize the network.
+          </Trans>
+        </Text>
+        {labelerInfo.creator.viewer?.blocking ? (
+          <View style={[a.flex_row, a.gap_sm, a.align_center, a.mt_md]}>
+            <CircleInfo size="sm" fill={t.atoms.text_contrast_medium.color} />
             <Text
-              style={[
-                a.pt_xl,
-                t.atoms.text_contrast_high,
-                a.leading_snug,
-                a.text_sm,
-              ]}>
+              style={[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.
+                Blocking does not prevent this labeler from placing labels on
+                your account.
               </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
+          </View>
+        ) : null}
+        {labelValues.length === 0 ? (
+          <Text
             style={[
-              a.mt_xl,
-              a.w_full,
-              a.rounded_md,
-              a.overflow_hidden,
-              t.atoms.bg_contrast_25,
+              a.pt_xl,
+              t.atoms.text_contrast_high,
+              a.leading_snug,
+              a.text_sm,
             ]}>
-            {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 style={{height: 100}} />
+            <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>
-    </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>
   )
 }