about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2024-09-24 20:10:13 -0500
committerGitHub <noreply@github.com>2024-09-24 20:10:13 -0500
commitb38d4697b7a42a5e4c48d86a6528a20ace9c034e (patch)
treed8f28c858044d45a15aaab741b668e38f8353528
parent2429d5d1ae51fa21d46970263fa581fd2eb19cdd (diff)
downloadvoidsky-b38d4697b7a42a5e4c48d86a6528a20ace9c034e.tar.zst
[Neue] Post avi, `PostMeta` cleanup (#5450)
* Support emoji in text with custom font

* Add emoji support to elements that need it

* Remove unused file causing lint failure

* Add web only link variant

* Refactor PostMeta

* Reduce avi size in feeds

* Fix alignment, emoji, in PostMeta

* Smaller avis in notifications

* Shrink post placeholder avi

* Handle the handle again

* Link cleanup

* Cleanup unused props

* Fix text wrapping in timestamp

* Fix underline color

* Tighten up spacing

* Web only whiteSpace
-rw-r--r--src/App.web.tsx4
-rw-r--r--src/components/Link.tsx36
-rw-r--r--src/screens/Messages/Conversation/MessageInputEmbed.tsx1
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx8
-rw-r--r--src/view/com/post/Post.tsx3
-rw-r--r--src/view/com/posts/FeedItem.tsx5
-rw-r--r--src/view/com/posts/FeedSlice.tsx6
-rw-r--r--src/view/com/util/LoadingPlaceholder.tsx10
-rw-r--r--src/view/com/util/PostMeta.tsx153
-rw-r--r--src/view/com/util/post-embeds/QuoteEmbed.tsx11
10 files changed, 120 insertions, 117 deletions
diff --git a/src/App.web.tsx b/src/App.web.tsx
index 7d98737a3..1664812d0 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -1,5 +1,5 @@
-import 'lib/sentry' // must be near top
-import 'view/icons'
+import '#/lib/sentry' // must be near top
+import '#/view/icons'
 import './style.css'
 
 import React, {useEffect, useState} from 'react'
diff --git a/src/components/Link.tsx b/src/components/Link.tsx
index 6c25faffb..c80b9f370 100644
--- a/src/components/Link.tsx
+++ b/src/components/Link.tsx
@@ -9,6 +9,7 @@ import {sanitizeUrl} from '@braintree/sanitize-url'
 import {StackActions, useLinkProps} from '@react-navigation/native'
 
 import {BSKY_DOWNLOAD_URL} from '#/lib/constants'
+import {useNavigationDeduped} from '#/lib/hooks/useNavigationDeduped'
 import {AllNavigatorParams} from '#/lib/routes/types'
 import {shareUrl} from '#/lib/sharing'
 import {
@@ -17,11 +18,10 @@ import {
   isExternalUrl,
   linkRequiresWarning,
 } from '#/lib/strings/url-helpers'
-import {isNative} from '#/platform/detection'
+import {isNative, isWeb} from '#/platform/detection'
 import {shouldClickOpenNewTab} from '#/platform/urls'
 import {useModalControls} from '#/state/modals'
 import {useOpenLink} from '#/state/preferences/in-app-browser'
-import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped'
 import {atoms as a, flatten, TextStyleProp, useTheme, web} from '#/alf'
 import {Button, ButtonProps} from '#/components/Button'
 import {useInteractionState} from '#/components/hooks/useInteractionState'
@@ -244,7 +244,10 @@ export function Link({
 export type InlineLinkProps = React.PropsWithChildren<
   BaseLinkProps & TextStyleProp & Pick<TextProps, 'selectable'>
 > &
-  Pick<ButtonProps, 'label'>
+  Pick<ButtonProps, 'label'> & {
+    disableUnderline?: boolean
+    title?: TextProps['title']
+  }
 
 export function InlineLinkText({
   children,
@@ -257,6 +260,7 @@ export function InlineLinkText({
   selectable,
   label,
   shareOnLongPress,
+  disableUnderline,
   ...rest
 }: InlineLinkProps) {
   const t = useTheme()
@@ -290,11 +294,12 @@ export function InlineLinkText({
       {...rest}
       style={[
         {color: t.palette.primary_500},
-        (hovered || focused || pressed) && {
-          ...web({outline: 0}),
-          textDecorationLine: 'underline',
-          textDecorationColor: flattenedStyle.color ?? t.palette.primary_500,
-        },
+        (hovered || focused || pressed) &&
+          !disableUnderline && {
+            ...web({outline: 0}),
+            textDecorationLine: 'underline',
+            textDecorationColor: flattenedStyle.color ?? t.palette.primary_500,
+          },
         flattenedStyle,
       ]}
       role="link"
@@ -365,3 +370,18 @@ export function BaseLink({
     </Pressable>
   )
 }
+
+export function WebOnlyInlineLinkText({
+  children,
+  to,
+  onPress,
+  ...props
+}: InlineLinkProps) {
+  return isWeb ? (
+    <InlineLinkText {...props} to={to} onPress={onPress}>
+      {children}
+    </InlineLinkText>
+  ) : (
+    <Text {...props}>{children}</Text>
+  )
+}
diff --git a/src/screens/Messages/Conversation/MessageInputEmbed.tsx b/src/screens/Messages/Conversation/MessageInputEmbed.tsx
index bf28ed4fe..2d1551019 100644
--- a/src/screens/Messages/Conversation/MessageInputEmbed.tsx
+++ b/src/screens/Messages/Conversation/MessageInputEmbed.tsx
@@ -174,7 +174,6 @@ export function MessageInputEmbed({
             showAvatar
             author={post.author}
             moderation={moderation}
-            authorHasWarning={!!post.author.labels?.length}
             timestamp={post.indexedAt}
             postHref={itemHref}
             style={a.flex_0}
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 3fb2309b9..ead9df116 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -558,18 +558,14 @@ let PostThreadItemLoaded = ({
               <PostMeta
                 author={post.author}
                 moderation={moderation}
-                authorHasWarning={!!post.author.labels?.length}
                 timestamp={post.indexedAt}
                 postHref={postHref}
                 showAvatar={isThreadedChild}
                 avatarModeration={moderation.ui('avatar')}
-                avatarSize={28}
-                displayNameType="md-bold"
-                displayNameStyle={isThreadedChild && s.ml2}
+                avatarSize={24}
                 style={
                   isThreadedChild && {
-                    alignItems: 'center',
-                    paddingBottom: isWeb ? 5 : 2,
+                    paddingBottom: isWeb ? 5 : 4,
                   }
                 }
               />
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index 9033fb96f..ec730a5e1 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -163,7 +163,7 @@ function PostInner({
         <View style={styles.layoutAvi}>
           <AviFollowButton author={post.author} moderation={moderation}>
             <PreviewableUserAvatar
-              size={52}
+              size={42}
               profile={post.author}
               moderation={moderation.ui('avatar')}
               type={post.author.associated?.labeler ? 'labeler' : 'user'}
@@ -174,7 +174,6 @@ function PostInner({
           <PostMeta
             author={post.author}
             moderation={moderation}
-            authorHasWarning={!!post.author.labels?.length}
             timestamp={post.indexedAt}
             postHref={itemHref}
           />
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index b1509b271..fb9cdb065 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -245,7 +245,7 @@ let FeedItemInner = ({
       onBeforePress={onBeforePress}
       dataSet={{feedContext}}>
       <View style={{flexDirection: 'row', gap: 10, paddingLeft: 8}}>
-        <View style={{width: 52}}>
+        <View style={{width: 42}}>
           {isThreadChild && (
             <View
               style={[
@@ -345,7 +345,7 @@ let FeedItemInner = ({
         <View style={styles.layoutAvi}>
           <AviFollowButton author={post.author} moderation={moderation}>
             <PreviewableUserAvatar
-              size={52}
+              size={42}
               profile={post.author}
               moderation={moderation.ui('avatar')}
               type={post.author.associated?.labeler ? 'labeler' : 'user'}
@@ -369,7 +369,6 @@ let FeedItemInner = ({
           <PostMeta
             author={post.author}
             moderation={moderation}
-            authorHasWarning={!!post.author.labels?.length}
             timestamp={post.indexedAt}
             postHref={href}
             onOpenAuthor={onOpenAuthor}
diff --git a/src/view/com/posts/FeedSlice.tsx b/src/view/com/posts/FeedSlice.tsx
index 0920026f6..dc68ee7a1 100644
--- a/src/view/com/posts/FeedSlice.tsx
+++ b/src/view/com/posts/FeedSlice.tsx
@@ -4,9 +4,9 @@ import Svg, {Circle, Line} from 'react-native-svg'
 import {AtUri} from '@atproto/api'
 import {Trans} from '@lingui/macro'
 
+import {usePalette} from '#/lib/hooks/usePalette'
+import {makeProfileLink} from '#/lib/routes/links'
 import {FeedPostSlice} from '#/state/queries/post-feed'
-import {usePalette} from 'lib/hooks/usePalette'
-import {makeProfileLink} from 'lib/routes/links'
 import {Link} from '../util/Link'
 import {Text} from '../util/text/Text'
 import {FeedItem} from './FeedItem'
@@ -146,7 +146,7 @@ const styles = StyleSheet.create({
     paddingLeft: 18,
   },
   viewFullThreadDots: {
-    width: 52,
+    width: 42,
     alignItems: 'center',
   },
 })
diff --git a/src/view/com/util/LoadingPlaceholder.tsx b/src/view/com/util/LoadingPlaceholder.tsx
index 6e75e88ca..6620eb8e2 100644
--- a/src/view/com/util/LoadingPlaceholder.tsx
+++ b/src/view/com/util/LoadingPlaceholder.tsx
@@ -7,9 +7,9 @@ import {
   ViewStyle,
 } from 'react-native'
 
-import {usePalette} from 'lib/hooks/usePalette'
-import {s} from 'lib/styles'
-import {useTheme} from 'lib/ThemeContext'
+import {usePalette} from '#/lib/hooks/usePalette'
+import {s} from '#/lib/styles'
+import {useTheme} from '#/lib/ThemeContext'
 import {atoms as a, useTheme as useTheme_NEW} from '#/alf'
 import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble'
 import {
@@ -53,8 +53,8 @@ export function PostLoadingPlaceholder({
   return (
     <View style={[styles.post, pal.view, style]}>
       <LoadingPlaceholder
-        width={52}
-        height={52}
+        width={42}
+        height={42}
         style={[
           styles.avatar,
           {
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index f2d717e96..3f647f978 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -1,44 +1,40 @@
 import React, {memo, useCallback} from 'react'
-import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
+import {StyleProp, View, ViewStyle} from 'react-native'
 import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api'
+import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useQueryClient} from '@tanstack/react-query'
 
-import {usePalette} from '#/lib/hooks/usePalette'
 import {makeProfileLink} from '#/lib/routes/links'
 import {forceLTR} from '#/lib/strings/bidi'
 import {NON_BREAKING_SPACE} from '#/lib/strings/constants'
 import {sanitizeDisplayName} from '#/lib/strings/display-names'
 import {sanitizeHandle} from '#/lib/strings/handles'
 import {niceDate} from '#/lib/strings/time'
-import {TypographyVariant} from '#/lib/ThemeContext'
-import {isAndroid} from '#/platform/detection'
 import {precacheProfile} from '#/state/queries/profile'
+import {atoms as a, useTheme, web} from '#/alf'
+import {WebOnlyInlineLinkText} from '#/components/Link'
 import {ProfileHoverCard} from '#/components/ProfileHoverCard'
-import {TextLinkOnWebOnly} from './Link'
-import {Text} from './text/Text'
+import {Text} from '#/components/Typography'
 import {TimeElapsed} from './TimeElapsed'
 import {PreviewableUserAvatar} from './UserAvatar'
 
 interface PostMetaOpts {
   author: AppBskyActorDefs.ProfileViewBasic
   moderation: ModerationDecision | undefined
-  authorHasWarning: boolean
   postHref: string
   timestamp: string
   showAvatar?: boolean
   avatarModeration?: ModerationUI
   avatarSize?: number
-  displayNameType?: TypographyVariant
-  displayNameStyle?: StyleProp<TextStyle>
   onOpenAuthor?: () => void
   style?: StyleProp<ViewStyle>
 }
 
 let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
-  const {i18n} = useLingui()
+  const t = useTheme()
+  const {i18n, _} = useLingui()
 
-  const pal = usePalette('default')
   const displayName = opts.author.displayName || opts.author.handle
   const handle = opts.author.handle
   const profileLink = makeProfileLink(opts.author)
@@ -53,9 +49,18 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
   }, [queryClient, opts.author])
 
   return (
-    <View style={[styles.container, opts.style]}>
+    <View
+      style={[
+        a.flex_1,
+        a.flex_row,
+        a.align_center,
+        a.pb_2xs,
+        a.gap_xs,
+        a.z_10,
+        opts.style,
+      ]}>
       {opts.showAvatar && (
-        <View style={styles.avatar}>
+        <View style={[a.self_center, a.mr_2xs]}>
           <PreviewableUserAvatar
             size={opts.avatarSize || 16}
             profile={opts.author}
@@ -65,63 +70,67 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
         </View>
       )}
       <ProfileHoverCard inline did={opts.author.did}>
-        <Text
-          numberOfLines={1}
-          style={[styles.maxWidth, pal.textLight, opts.displayNameStyle]}>
-          <TextLinkOnWebOnly
-            type={opts.displayNameType || 'lg-bold'}
-            style={[pal.text]}
-            lineHeight={1.2}
+        <Text numberOfLines={1} style={[a.flex_shrink]}>
+          <WebOnlyInlineLinkText
+            to={profileLink}
+            label={_(msg`View profile`)}
             disableMismatchWarning
-            text={
-              <Text
-                type={opts.displayNameType || 'lg-bold'}
-                emoji
-                style={[pal.text]}
-                lineHeight={1.2}>
-                {forceLTR(
-                  sanitizeDisplayName(
-                    displayName,
-                    opts.moderation?.ui('displayName'),
-                  ),
-                )}
-              </Text>
-            }
-            href={profileLink}
-            onBeforePress={onBeforePressAuthor}
-          />
-          <TextLinkOnWebOnly
-            type="md"
+            onPress={onBeforePressAuthor}
+            style={[t.atoms.text]}>
+            <Text emoji style={[a.text_md, a.font_bold, a.leading_tight]}>
+              {forceLTR(
+                sanitizeDisplayName(
+                  displayName,
+                  opts.moderation?.ui('displayName'),
+                ),
+              )}
+            </Text>
+          </WebOnlyInlineLinkText>
+          <WebOnlyInlineLinkText
+            to={profileLink}
+            label={_(msg`View profile`)}
             disableMismatchWarning
-            style={[pal.textLight, {flexShrink: 4}]}
-            text={
-              <Text emoji style={[pal.textLight, {flexShrink: 4}]}>
-                {NON_BREAKING_SPACE + sanitizeHandle(handle, '@')}
-              </Text>
-            }
-            href={profileLink}
-            onBeforePress={onBeforePressAuthor}
-            anchorNoUnderline
-          />
+            disableUnderline
+            onPress={onBeforePressAuthor}
+            style={[a.text_md, t.atoms.text_contrast_medium, a.leading_tight]}>
+            <Text
+              emoji
+              style={[
+                a.text_md,
+                t.atoms.text_contrast_medium,
+                a.leading_tight,
+              ]}>
+              {NON_BREAKING_SPACE + sanitizeHandle(handle, '@')}
+            </Text>
+          </WebOnlyInlineLinkText>
         </Text>
       </ProfileHoverCard>
-      {!isAndroid && (
-        <Text type="md" style={pal.textLight} accessible={false}>
-          &middot;
-        </Text>
-      )}
+
+      <Text
+        style={[a.text_md, t.atoms.text_contrast_medium]}
+        accessible={false}>
+        &middot;
+      </Text>
+
       <TimeElapsed timestamp={opts.timestamp}>
         {({timeElapsed}) => (
-          <TextLinkOnWebOnly
-            type="md"
-            style={pal.textLight}
-            text={timeElapsed}
-            accessibilityLabel={niceDate(i18n, opts.timestamp)}
+          <WebOnlyInlineLinkText
+            to={opts.postHref}
+            label={niceDate(i18n, opts.timestamp)}
             title={niceDate(i18n, opts.timestamp)}
-            accessibilityHint=""
-            href={opts.postHref}
-            onBeforePress={onBeforePressPost}
-          />
+            disableMismatchWarning
+            disableUnderline
+            onPress={onBeforePressPost}
+            style={[
+              a.text_md,
+              t.atoms.text_contrast_medium,
+              a.leading_tight,
+              web({
+                whiteSpace: 'nowrap',
+              }),
+            ]}>
+            {timeElapsed}
+          </WebOnlyInlineLinkText>
         )}
       </TimeElapsed>
     </View>
@@ -129,21 +138,3 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
 }
 PostMeta = memo(PostMeta)
 export {PostMeta}
-
-const styles = StyleSheet.create({
-  container: {
-    flexDirection: 'row',
-    alignItems: 'flex-end',
-    paddingBottom: 2,
-    gap: 4,
-    zIndex: 1,
-    flex: 1,
-  },
-  avatar: {
-    alignSelf: 'center',
-  },
-  maxWidth: {
-    flex: isAndroid ? 1 : undefined,
-    flexShrink: isAndroid ? undefined : 1,
-  },
-})
diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx
index 79e326404..3b8152c8b 100644
--- a/src/view/com/util/post-embeds/QuoteEmbed.tsx
+++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx
@@ -24,15 +24,15 @@ import {useLingui} from '@lingui/react'
 import {useQueryClient} from '@tanstack/react-query'
 
 import {HITSLOP_20} from '#/lib/constants'
+import {usePalette} from '#/lib/hooks/usePalette'
+import {InfoCircleIcon} from '#/lib/icons'
 import {moderatePost_wrapped} from '#/lib/moderatePost_wrapped'
+import {makeProfileLink} from '#/lib/routes/links'
 import {s} from '#/lib/styles'
 import {useModerationOpts} from '#/state/preferences/moderation-opts'
+import {precacheProfile} from '#/state/queries/profile'
 import {useSession} from '#/state/session'
-import {usePalette} from 'lib/hooks/usePalette'
-import {InfoCircleIcon} from 'lib/icons'
-import {makeProfileLink} from 'lib/routes/links'
-import {precacheProfile} from 'state/queries/profile'
-import {ComposerOptsQuote} from 'state/shell/composer'
+import {ComposerOptsQuote} from '#/state/shell/composer'
 import {atoms as a, useTheme} from '#/alf'
 import {RichText} from '#/components/RichText'
 import {ContentHider} from '../../../../components/moderation/ContentHider'
@@ -238,7 +238,6 @@ export function QuoteEmbed({
             author={quote.author}
             moderation={moderation}
             showAvatar
-            authorHasWarning={false}
             postHref={itemHref}
             timestamp={quote.indexedAt}
           />