about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/components/FeedInterstitials.tsx64
-rw-r--r--src/components/ProfileCard.tsx39
-rw-r--r--src/lib/api/feed-manip.ts27
-rw-r--r--src/lib/statsig/gates.ts1
-rw-r--r--src/screens/Profile/Header/ProfileHeaderStandard.tsx2
-rw-r--r--src/state/queries/post-thread.ts8
-rw-r--r--src/state/queries/suggested-follows.ts7
-rw-r--r--src/view/com/composer/Composer.tsx15
-rw-r--r--src/view/com/composer/videos/SubtitleFilePicker.tsx10
-rw-r--r--src/view/com/profile/FollowButton.tsx11
-rw-r--r--src/view/com/util/images/AutoSizedImage.tsx69
-rw-r--r--src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx4
-rw-r--r--src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx2
-rw-r--r--src/view/screens/Profile.tsx11
14 files changed, 162 insertions, 108 deletions
diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx
index 5031f584e..286ded13e 100644
--- a/src/components/FeedInterstitials.tsx
+++ b/src/components/FeedInterstitials.tsx
@@ -60,16 +60,13 @@ function CardOuter({
 export function SuggestedFollowPlaceholder() {
   const t = useTheme()
   return (
-    <CardOuter style={[a.gap_sm, t.atoms.border_contrast_low]}>
+    <CardOuter style={[a.gap_md, t.atoms.border_contrast_low]}>
       <ProfileCard.Header>
         <ProfileCard.AvatarPlaceholder />
-      </ProfileCard.Header>
-
-      <View style={[a.py_xs]}>
         <ProfileCard.NameAndHandlePlaceholder />
-      </View>
+      </ProfileCard.Header>
 
-      <ProfileCard.DescriptionPlaceholder />
+      <ProfileCard.DescriptionPlaceholder numberOfLines={2} />
     </CardOuter>
   )
 }
@@ -176,9 +173,14 @@ function useExperimentalSuggestedUsersQuery() {
 }
 
 export function SuggestedFollows({feed}: {feed: FeedDescriptor}) {
-  const [feedType, feedUri] = feed.split('|')
+  const {currentAccount} = useSession()
+  const [feedType, feedUriOrDid] = feed.split('|')
   if (feedType === 'author') {
-    return <SuggestedFollowsProfile did={feedUri} />
+    if (currentAccount?.did === feedUriOrDid) {
+      return null
+    } else {
+      return <SuggestedFollowsProfile did={feedUriOrDid} />
+    }
   } else {
     return <SuggestedFollowsHome />
   }
@@ -197,6 +199,7 @@ export function SuggestedFollowsProfile({did}: {did: string}) {
       isSuggestionsLoading={isSuggestionsLoading}
       profiles={data?.suggestions ?? []}
       error={error}
+      viewContext="profile"
     />
   )
 }
@@ -212,6 +215,7 @@ export function SuggestedFollowsHome() {
       isSuggestionsLoading={isSuggestionsLoading}
       profiles={profiles}
       error={error}
+      viewContext="feed"
     />
   )
 }
@@ -220,10 +224,12 @@ export function ProfileGrid({
   isSuggestionsLoading,
   error,
   profiles,
+  viewContext = 'feed',
 }: {
   isSuggestionsLoading: boolean
   profiles: AppBskyActorDefs.ProfileViewDetailed[]
   error: Error | null
+  viewContext: 'profile' | 'feed'
 }) {
   const t = useTheme()
   const {_} = useLingui()
@@ -280,7 +286,7 @@ export function ProfileGrid({
                     shape="round"
                   />
                 </ProfileCard.Header>
-                <ProfileCard.Description profile={profile} />
+                <ProfileCard.Description profile={profile} numberOfLines={2} />
               </ProfileCard.Outer>
             </CardOuter>
           )}
@@ -297,33 +303,31 @@ export function ProfileGrid({
   return (
     <View
       style={[a.border_t, t.atoms.border_contrast_low, t.atoms.bg_contrast_25]}>
-      <View style={[a.pt_2xl, a.px_lg, a.flex_row, a.pb_xs]}>
-        <Text
-          style={[
-            a.flex_1,
-            a.text_lg,
-            a.font_bold,
-            t.atoms.text_contrast_medium,
-          ]}>
-          <Trans>Suggested for you</Trans>
+      <View
+        style={[
+          a.p_lg,
+          a.pb_xs,
+          a.flex_row,
+          a.align_center,
+          a.justify_between,
+        ]}>
+        <Text style={[a.text_sm, a.font_bold, t.atoms.text_contrast_medium]}>
+          {viewContext === 'profile' ? (
+            <Trans>Similar accounts</Trans>
+          ) : (
+            <Trans>Suggested for you</Trans>
+          )}
         </Text>
-        <Person fill={t.atoms.text_contrast_low.color} />
+        <Person fill={t.atoms.text_contrast_low.color} size="sm" />
       </View>
 
       {gtMobile ? (
-        <View style={[a.flex_1, a.px_lg, a.pt_md, a.pb_xl, a.gap_md]}>
-          <View style={[a.flex_1, a.flex_row, a.flex_wrap, a.gap_md]}>
+        <View style={[a.flex_1, a.px_lg, a.pt_sm, a.pb_lg, a.gap_md]}>
+          <View style={[a.flex_1, a.flex_row, a.flex_wrap, a.gap_sm]}>
             {content}
           </View>
 
-          <View
-            style={[
-              a.flex_row,
-              a.justify_end,
-              a.align_center,
-              a.pt_xs,
-              a.gap_md,
-            ]}>
+          <View style={[a.flex_row, a.justify_end, a.align_center, a.gap_md]}>
             <InlineLinkText
               label={_(msg`Browse more suggestions`)}
               to="/search"
@@ -339,7 +343,7 @@ export function ProfileGrid({
           showsHorizontalScrollIndicator={false}
           snapToInterval={MOBILE_CARD_WIDTH + a.gap_md.gap}
           decelerationRate="fast">
-          <View style={[a.px_lg, a.pt_md, a.pb_xl, a.flex_row, a.gap_md]}>
+          <View style={[a.px_lg, a.pt_sm, a.pb_lg, a.flex_row, a.gap_md]}>
             {content}
 
             <Button
diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx
index 6f6d68049..b208903b4 100644
--- a/src/components/ProfileCard.tsx
+++ b/src/components/ProfileCard.tsx
@@ -220,8 +220,10 @@ export function NameAndHandlePlaceholder() {
 
 export function Description({
   profile: profileUnshadowed,
+  numberOfLines = 3,
 }: {
   profile: AppBskyActorDefs.ProfileViewDetailed
+  numberOfLines?: number
 }) {
   const profile = useProfileShadow(profileUnshadowed)
   const {description} = profile
@@ -244,31 +246,34 @@ export function Description({
       <RichText
         value={rt}
         style={[a.leading_snug]}
-        numberOfLines={3}
+        numberOfLines={numberOfLines}
         disableLinks
       />
     </View>
   )
 }
 
-export function DescriptionPlaceholder() {
+export function DescriptionPlaceholder({
+  numberOfLines = 3,
+}: {
+  numberOfLines?: number
+}) {
   const t = useTheme()
   return (
-    <View style={[a.gap_xs]}>
-      <View
-        style={[a.rounded_xs, a.w_full, t.atoms.bg_contrast_50, {height: 12}]}
-      />
-      <View
-        style={[a.rounded_xs, a.w_full, t.atoms.bg_contrast_50, {height: 12}]}
-      />
-      <View
-        style={[
-          a.rounded_xs,
-          a.w_full,
-          t.atoms.bg_contrast_50,
-          {height: 12, width: 100},
-        ]}
-      />
+    <View style={[{gap: 8}]}>
+      {Array(numberOfLines)
+        .fill(0)
+        .map((_, i) => (
+          <View
+            key={i}
+            style={[
+              a.rounded_xs,
+              a.w_full,
+              t.atoms.bg_contrast_50,
+              {height: 12, width: i + 1 === numberOfLines ? '60%' : '100%'},
+            ]}
+          />
+        ))}
     </View>
   )
 }
diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts
index eaa760b4b..0eca8b165 100644
--- a/src/lib/api/feed-manip.ts
+++ b/src/lib/api/feed-manip.ts
@@ -379,7 +379,11 @@ export class FeedTuner {
     ): FeedViewPostsSlice[] => {
       for (let i = 0; i < slices.length; i++) {
         const slice = slices[i]
-        if (slice.isReply && !shouldDisplayReplyInFollowing(slice, userDid)) {
+        if (
+          slice.isReply &&
+          !slice.isRepost &&
+          !shouldDisplayReplyInFollowing(slice.getAuthors(), userDid)
+        ) {
           slices.splice(i, 1)
           i--
         }
@@ -443,13 +447,9 @@ function areSameAuthor(authors: AuthorContext): boolean {
 }
 
 function shouldDisplayReplyInFollowing(
-  slice: FeedViewPostsSlice,
+  authors: AuthorContext,
   userDid: string,
 ): boolean {
-  if (slice.isRepost) {
-    return true
-  }
-  const authors = slice.getAuthors()
   const {author, parentAuthor, grandparentAuthor, rootAuthor} = authors
   if (!isSelfOrFollowing(author, userDid)) {
     // Only show replies from self or people you follow.
@@ -463,21 +463,6 @@ function shouldDisplayReplyInFollowing(
     // Always show self-threads.
     return true
   }
-  if (
-    parentAuthor &&
-    parentAuthor.did !== author.did &&
-    rootAuthor &&
-    rootAuthor.did === author.did &&
-    slice.items.length > 2
-  ) {
-    // If you follow A, show A -> someone[>0 likes] -> A chains too.
-    // This is different from cases below because you only know one person.
-    const parentPost = slice.items[1].post
-    const parentLikeCount = parentPost.likeCount ?? 0
-    if (parentLikeCount > 0) {
-      return true
-    }
-  }
   // From this point on we need at least one more reason to show it.
   if (
     parentAuthor &&
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index 9b6c036b9..909b93e6b 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -3,4 +3,3 @@ export type Gate =
   | 'debug_show_feedcontext'
   | 'suggested_feeds_interstitial'
   | 'ten_million_dialog'
-  | 'video_upload' // upload videos
diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
index cf5fcb97e..846fa4424 100644
--- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx
+++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
@@ -219,6 +219,8 @@ let ProfileHeaderStandard = ({
                 <ButtonText>
                   {profile.viewer?.following ? (
                     <Trans>Following</Trans>
+                  ) : profile.viewer?.followedBy ? (
+                    <Trans>Follow Back</Trans>
                   ) : (
                     <Trans>Follow</Trans>
                   )}
diff --git a/src/state/queries/post-thread.ts b/src/state/queries/post-thread.ts
index 83ca60c2a..a569cb160 100644
--- a/src/state/queries/post-thread.ts
+++ b/src/state/queries/post-thread.ts
@@ -408,10 +408,14 @@ export function* findAllPostsInQueryData(
       }
     }
   }
-  for (let post of findAllPostsInFeedQueryData(queryClient, uri)) {
+  for (let post of findAllPostsInNotifsQueryData(queryClient, uri)) {
+    // Check notifications first. If you have a post in notifications,
+    // it's often due to a like or a repost, and we want to prioritize
+    // a post object with >0 likes/reposts over a stale version with no
+    // metrics in order to avoid a notification->post scroll jump.
     yield postViewToPlaceholderThread(post)
   }
-  for (let post of findAllPostsInNotifsQueryData(queryClient, uri)) {
+  for (let post of findAllPostsInFeedQueryData(queryClient, uri)) {
     yield postViewToPlaceholderThread(post)
   }
   for (let post of findAllPostsInQuoteQueryData(queryClient, uri)) {
diff --git a/src/state/queries/suggested-follows.ts b/src/state/queries/suggested-follows.ts
index f5d51a974..5ae831704 100644
--- a/src/state/queries/suggested-follows.ts
+++ b/src/state/queries/suggested-follows.ts
@@ -106,13 +106,16 @@ export function useSuggestedFollowsQuery(options?: SuggestedFollowsOptions) {
 export function useSuggestedFollowsByActorQuery({did}: {did: string}) {
   const agent = useAgent()
   return useQuery<AppBskyGraphGetSuggestedFollowsByActor.OutputSchema, Error>({
-    gcTime: 0,
     queryKey: suggestedFollowsByActorQueryKey(did),
     queryFn: async () => {
       const res = await agent.app.bsky.graph.getSuggestedFollowsByActor({
         actor: did,
       })
-      return res.data
+      const data = res.data.isFallback ? {suggestions: []} : res.data
+      data.suggestions = data.suggestions.filter(profile => {
+        return !profile.viewer?.following
+      })
+      return data
     },
   })
 }
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 4c7892bc0..dfdfb3ebd 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -59,7 +59,7 @@ import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible'
 import {usePalette} from '#/lib/hooks/usePalette'
 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
 import {LikelyType} from '#/lib/link-meta/link-meta'
-import {logEvent, useGate} from '#/lib/statsig/statsig'
+import {logEvent} from '#/lib/statsig/statsig'
 import {cleanError} from '#/lib/strings/errors'
 import {insertMentionAt} from '#/lib/strings/mention-manip'
 import {shortenLinks} from '#/lib/strings/rich-text-manip'
@@ -140,7 +140,6 @@ export const ComposePost = observer(function ComposePost({
 }: Props & {
   cancelRef?: React.RefObject<CancelRef>
 }) {
-  const gate = useGate()
   const {currentAccount} = useSession()
   const agent = useAgent()
   const {data: currentProfile} = useProfileQuery({did: currentAccount!.did})
@@ -803,13 +802,11 @@ export const ComposePost = observer(function ComposePost({
           ) : (
             <ToolbarWrapper style={[a.flex_row, a.align_center, a.gap_xs]}>
               <SelectPhotoBtn gallery={gallery} disabled={!canSelectImages} />
-              {gate('video_upload') && (
-                <SelectVideoBtn
-                  onSelectVideo={selectVideo}
-                  disabled={!canSelectImages}
-                  setError={setError}
-                />
-              )}
+              <SelectVideoBtn
+                onSelectVideo={selectVideo}
+                disabled={!canSelectImages}
+                setError={setError}
+              />
               <OpenCameraBtn gallery={gallery} disabled={!canSelectImages} />
               <SelectGifBtn
                 onClose={focusTextInput}
diff --git a/src/view/com/composer/videos/SubtitleFilePicker.tsx b/src/view/com/composer/videos/SubtitleFilePicker.tsx
index 9e0fe0aee..beb3f07a8 100644
--- a/src/view/com/composer/videos/SubtitleFilePicker.tsx
+++ b/src/view/com/composer/videos/SubtitleFilePicker.tsx
@@ -3,6 +3,7 @@ import {View} from 'react-native'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
+import {logger} from '#/logger'
 import * as Toast from '#/view/com/util/Toast'
 import {atoms as a} from '#/alf'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
@@ -25,9 +26,16 @@ export function SubtitleFilePicker({
   const handlePick = (evt: React.ChangeEvent<HTMLInputElement>) => {
     const selectedFile = evt.target.files?.[0]
     if (selectedFile) {
-      if (selectedFile.type === 'text/vtt') {
+      if (
+        selectedFile.type === 'text/vtt' ||
+        (selectedFile.type === 'text/plain' &&
+          selectedFile.name.endsWith('.vtt'))
+      ) {
         onSelectFile(selectedFile)
       } else {
+        logger.error('Invalid subtitle file type', {
+          safeMessage: `File: ${selectedFile.name} (${selectedFile.type})`,
+        })
         Toast.show(_(msg`Only WebVTT (.vtt) files are supported`))
       }
     }
diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx
index 42adea3cf..aaa5d3454 100644
--- a/src/view/com/profile/FollowButton.tsx
+++ b/src/view/com/profile/FollowButton.tsx
@@ -61,7 +61,7 @@ export function FollowButton({
         label={_(msg({message: 'Unfollow', context: 'action'}))}
       />
     )
-  } else {
+  } else if (!profile.viewer.followedBy) {
     return (
       <Button
         type={unfollowedType}
@@ -70,5 +70,14 @@ export function FollowButton({
         label={_(msg({message: 'Follow', context: 'action'}))}
       />
     )
+  } else {
+    return (
+      <Button
+        type={unfollowedType}
+        labelStyle={labelStyle}
+        onPress={onPressFollow}
+        label={_(msg({message: 'Follow Back', context: 'action'}))}
+      />
+    )
   }
 }
diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx
index f57ab4e3c..932a18280 100644
--- a/src/view/com/util/images/AutoSizedImage.tsx
+++ b/src/view/com/util/images/AutoSizedImage.tsx
@@ -10,7 +10,7 @@ import {Dimensions} from '#/lib/media/types'
 import {isNative} from '#/platform/detection'
 import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
 import {atoms as a, useBreakpoints, useTheme} from '#/alf'
-import {Crop_Stroke2_Corner0_Rounded as Crop} from '#/components/icons/Crop'
+import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal'
 import {Text} from '#/components/Typography'
 
 export function useImageAspectRatio({
@@ -23,9 +23,10 @@ export function useImageAspectRatio({
   const [raw, setAspectRatio] = React.useState<number>(
     dimensions ? calc(dimensions) : 1,
   )
+  // this basically controls the width of the image
   const {isCropped, constrained, max} = React.useMemo(() => {
-    const a34 = 0.75 // max of 3:4 ratio in feeds
-    const constrained = Math.max(raw, a34)
+    const ratio = 1 / 2 // max of 1:2 ratio in feeds
+    const constrained = Math.max(raw, ratio)
     const max = Math.max(raw, 0.25) // max of 1:4 in thread
     const isCropped = raw < constrained
     return {
@@ -68,14 +69,14 @@ export function ConstrainedImage({
   const t = useTheme()
   const {gtMobile} = useBreakpoints()
   /**
-   * Computed as a % value to apply as `paddingTop`
+   * Computed as a % value to apply as `paddingTop`, this basically controls
+   * the height of the image.
    */
   const outerAspectRatio = React.useMemo<DimensionValue>(() => {
-    // capped to square or shorter
     const ratio =
       isNative || !gtMobile
-        ? Math.min(1 / aspectRatio, 1.5)
-        : Math.min(1 / aspectRatio, 1)
+        ? Math.min(1 / aspectRatio, 16 / 9) // 9:16 bounding box
+        : Math.min(1 / aspectRatio, 1) // 1:1 bounding box
     return `${ratio * 100}%`
   }, [aspectRatio, gtMobile])
 
@@ -146,33 +147,59 @@ export function AutoSizedImage({
           style={[
             a.absolute,
             a.flex_row,
-            a.align_center,
-            a.rounded_xs,
-            t.atoms.bg_contrast_25,
             {
-              gap: 3,
-              padding: 3,
               bottom: a.p_xs.padding,
               right: a.p_xs.padding,
-              opacity: 0.8,
+              gap: 3,
             },
             largeAlt && [
               {
                 gap: 4,
-                padding: 5,
               },
             ],
           ]}>
           {isCropped && (
-            <Crop
-              fill={t.atoms.text_contrast_high.color}
-              width={largeAlt ? 18 : 12}
-            />
+            <View
+              style={[
+                a.rounded_xs,
+                t.atoms.bg_contrast_25,
+                {
+                  padding: 3,
+                  opacity: 0.8,
+                },
+                largeAlt && [
+                  {
+                    padding: 5,
+                  },
+                ],
+              ]}>
+              <Fullscreen
+                fill={t.atoms.text_contrast_high.color}
+                width={largeAlt ? 18 : 12}
+              />
+            </View>
           )}
           {hasAlt && (
-            <Text style={[a.font_heavy, largeAlt ? a.text_xs : {fontSize: 8}]}>
-              ALT
-            </Text>
+            <View
+              style={[
+                a.justify_center,
+                a.rounded_xs,
+                t.atoms.bg_contrast_25,
+                {
+                  padding: 3,
+                  opacity: 0.8,
+                },
+                largeAlt && [
+                  {
+                    padding: 5,
+                  },
+                ],
+              ]}>
+              <Text
+                style={[a.font_heavy, largeAlt ? a.text_xs : {fontSize: 8}]}>
+                ALT
+              </Text>
+            </View>
           )}
         </View>
       ) : null}
diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx
index 6636883f1..be3f90711 100644
--- a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx
@@ -37,11 +37,11 @@ export function TimeIndicator({time}: {time: number}) {
       ]}>
       <Text
         style={[
-          {color: t.palette.white, fontSize: 12},
+          {color: t.palette.white, fontSize: 12, fontVariant: ['tabular-nums']},
           a.font_bold,
           {lineHeight: 1.25},
         ]}>
-        {minutes}:{seconds}
+        {`${minutes}:${seconds}`}
       </Text>
     </Animated.View>
   )
diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx
index bb15db083..791025f70 100644
--- a/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx
@@ -370,7 +370,7 @@ export function Controls({
             onPress={onPressPlayPause}
           />
           <View style={a.flex_1} />
-          <Text style={{color: t.palette.white}}>
+          <Text style={{color: t.palette.white, fontVariant: ['tabular-nums']}}>
             {formatTime(currentTime)} / {formatTime(duration)}
           </Text>
           {hasSubtitleTrack && (
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 37111c02e..5ef645981 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -41,6 +41,7 @@ import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed'
 import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels'
 import {ScreenHider} from '#/components/moderation/ScreenHider'
 import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
+import {navigate} from '#/Navigation'
 import {ExpoScrollForwarderView} from '../../../modules/expo-scroll-forwarder'
 import {ProfileFeedgens} from '../com/feeds/ProfileFeedgens'
 import {ProfileLists} from '../com/lists/ProfileLists'
@@ -86,6 +87,16 @@ export function ProfileScreen({route}: Props) {
     }
   }, [resolveError, refetchDid, refetchProfile])
 
+  // Apply hard-coded redirects as need
+  React.useEffect(() => {
+    if (resolveError) {
+      if (name === 'lulaoficial.bsky.social') {
+        console.log('Applying redirect to lula.com.br')
+        navigate('Profile', {name: 'lula.com.br'})
+      }
+    }
+  }, [name, resolveError])
+
   // When we open the profile, we want to reset the posts query if we are blocked.
   React.useEffect(() => {
     if (resolvedDid && profile?.viewer?.blockedBy) {