about summary refs log tree commit diff
path: root/src/view/com/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/util')
-rw-r--r--src/view/com/util/PostMeta.tsx44
-rw-r--r--src/view/com/util/UserInfoText.tsx31
-rw-r--r--src/view/com/util/post-embeds/ExternalLinkEmbed.tsx27
-rw-r--r--src/view/com/util/text/Text.tsx50
-rw-r--r--src/view/com/util/text/ThemedText.tsx80
5 files changed, 97 insertions, 135 deletions
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index 3bd350bf3..f2d717e96 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -4,16 +4,16 @@ import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api'
 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 {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 {ProfileHoverCard} from '#/components/ProfileHoverCard'
 import {TextLinkOnWebOnly} from './Link'
 import {Text} from './text/Text'
@@ -73,12 +73,20 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
             style={[pal.text]}
             lineHeight={1.2}
             disableMismatchWarning
-            text={forceLTR(
-              sanitizeDisplayName(
-                displayName,
-                opts.moderation?.ui('displayName'),
-              ),
-            )}
+            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}
           />
@@ -86,7 +94,11 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
             type="md"
             disableMismatchWarning
             style={[pal.textLight, {flexShrink: 4}]}
-            text={NON_BREAKING_SPACE + sanitizeHandle(handle, '@')}
+            text={
+              <Text emoji style={[pal.textLight, {flexShrink: 4}]}>
+                {NON_BREAKING_SPACE + sanitizeHandle(handle, '@')}
+              </Text>
+            }
             href={profileLink}
             onBeforePress={onBeforePressAuthor}
             anchorNoUnderline
diff --git a/src/view/com/util/UserInfoText.tsx b/src/view/com/util/UserInfoText.tsx
index 9cb9997f6..8a444d590 100644
--- a/src/view/com/util/UserInfoText.tsx
+++ b/src/view/com/util/UserInfoText.tsx
@@ -1,15 +1,16 @@
 import React from 'react'
-import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
 import {StyleProp, StyleSheet, TextStyle} from 'react-native'
+import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
+
+import {makeProfileLink} from '#/lib/routes/links'
+import {sanitizeDisplayName} from '#/lib/strings/display-names'
+import {sanitizeHandle} from '#/lib/strings/handles'
+import {TypographyVariant} from '#/lib/ThemeContext'
+import {STALE} from '#/state/queries'
+import {useProfileQuery} from '#/state/queries/profile'
 import {TextLinkOnWebOnly} from './Link'
-import {Text} from './text/Text'
 import {LoadingPlaceholder} from './LoadingPlaceholder'
-import {TypographyVariant} from 'lib/ThemeContext'
-import {sanitizeDisplayName} from 'lib/strings/display-names'
-import {sanitizeHandle} from 'lib/strings/handles'
-import {makeProfileLink} from 'lib/routes/links'
-import {useProfileQuery} from '#/state/queries/profile'
-import {STALE} from '#/state/queries'
+import {Text} from './text/Text'
 
 export function UserInfoText({
   type = 'md',
@@ -50,11 +51,15 @@ export function UserInfoText({
         lineHeight={1.2}
         numberOfLines={1}
         href={makeProfileLink(profile)}
-        text={`${prefix || ''}${sanitizeDisplayName(
-          typeof profile[attr] === 'string' && profile[attr]
-            ? (profile[attr] as string)
-            : sanitizeHandle(profile.handle),
-        )}`}
+        text={
+          <Text emoji type={type} style={style} lineHeight={1.2}>
+            {`${prefix || ''}${sanitizeDisplayName(
+              typeof profile[attr] === 'string' && profile[attr]
+                ? (profile[attr] as string)
+                : sanitizeHandle(profile.handle),
+            )}`}
+          </Text>
+        }
       />
     )
   } else {
diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
index 54e1eb4d5..98332c33b 100644
--- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
+++ b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
@@ -5,21 +5,21 @@ import {AppBskyEmbedExternal} from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
-import {usePalette} from 'lib/hooks/usePalette'
-import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {shareUrl} from 'lib/sharing'
-import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player'
+import {usePalette} from '#/lib/hooks/usePalette'
+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {shareUrl} from '#/lib/sharing'
+import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player'
 import {
   getStarterPackOgCard,
   parseStarterPackUri,
-} from 'lib/strings/starter-pack'
-import {toNiceDomain} from 'lib/strings/url-helpers'
-import {isNative} from 'platform/detection'
-import {useExternalEmbedsPrefs} from 'state/preferences'
-import {Link} from 'view/com/util/Link'
-import {ExternalGifEmbed} from 'view/com/util/post-embeds/ExternalGifEmbed'
-import {ExternalPlayer} from 'view/com/util/post-embeds/ExternalPlayerEmbed'
-import {GifEmbed} from 'view/com/util/post-embeds/GifEmbed'
+} from '#/lib/strings/starter-pack'
+import {toNiceDomain} from '#/lib/strings/url-helpers'
+import {isNative} from '#/platform/detection'
+import {useExternalEmbedsPrefs} from '#/state/preferences'
+import {Link} from '#/view/com/util/Link'
+import {ExternalGifEmbed} from '#/view/com/util/post-embeds/ExternalGifEmbed'
+import {ExternalPlayer} from '#/view/com/util/post-embeds/ExternalPlayerEmbed'
+import {GifEmbed} from '#/view/com/util/post-embeds/GifEmbed'
 import {atoms as a, useTheme} from '#/alf'
 import {MediaInsetBorder} from '#/components/MediaInsetBorder'
 import {Text} from '../text/Text'
@@ -115,12 +115,13 @@ export const ExternalLinkEmbed = ({
           </Text>
 
           {!embedPlayerParams?.isGif && !embedPlayerParams?.dimensions && (
-            <Text type="lg-bold" numberOfLines={3} style={[pal.text]}>
+            <Text emoji type="lg-bold" numberOfLines={3} style={[pal.text]}>
               {link.title || link.uri}
             </Text>
           )}
           {link.description ? (
             <Text
+              emoji
               type="md"
               numberOfLines={link.thumb ? 2 : 4}
               style={[pal.text, a.mt_xs]}>
diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx
index 52a45b0e2..3d885480c 100644
--- a/src/view/com/util/text/Text.tsx
+++ b/src/view/com/util/text/Text.tsx
@@ -2,27 +2,40 @@ import React from 'react'
 import {StyleSheet, Text as RNText, TextProps} from 'react-native'
 import {UITextView} from 'react-native-uitextview'
 
-import {lh, s} from 'lib/styles'
-import {TypographyVariant, useTheme} from 'lib/ThemeContext'
-import {isIOS, isWeb} from 'platform/detection'
+import {lh, s} from '#/lib/styles'
+import {TypographyVariant, useTheme} from '#/lib/ThemeContext'
+import {logger} from '#/logger'
+import {isIOS} from '#/platform/detection'
 import {applyFonts, useAlf} from '#/alf'
+import {
+  childHasEmoji,
+  childIsString,
+  renderChildrenWithEmoji,
+  StringChild,
+} from '#/components/Typography'
+import {IS_DEV} from '#/env'
 
-export type CustomTextProps = TextProps & {
+export type CustomTextProps = Omit<TextProps, 'children'> & {
   type?: TypographyVariant
   lineHeight?: number
   title?: string
   dataSet?: Record<string, string | number>
   selectable?: boolean
-}
-
-const fontFamilyStyle = {
-  fontFamily:
-    '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif',
-}
+} & (
+    | {
+        emoji: true
+        children: StringChild
+      }
+    | {
+        emoji?: false
+        children: TextProps['children']
+      }
+  )
 
 export function Text({
   type = 'md',
   children,
+  emoji,
   lineHeight,
   style,
   title,
@@ -35,6 +48,18 @@ export function Text({
   const lineHeightStyle = lineHeight ? lh(theme, type, lineHeight) : undefined
   const {fonts} = useAlf()
 
+  if (IS_DEV) {
+    if (!emoji && childHasEmoji(children)) {
+      logger.warn(
+        `Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add <Text emoji />'`,
+      )
+    }
+
+    if (emoji && !childIsString(children)) {
+      logger.error('Text: when <Text emoji />, children can only be strings.')
+    }
+  }
+
   if (selectable && isIOS) {
     const flattened = StyleSheet.flatten([
       s.black,
@@ -58,7 +83,7 @@ export function Text({
         selectable={selectable}
         uiTextView
         {...props}>
-        {children}
+        {isIOS && emoji ? renderChildrenWithEmoji(children) : children}
       </UITextView>
     )
   }
@@ -66,7 +91,6 @@ export function Text({
   const flattened = StyleSheet.flatten([
     s.black,
     typography,
-    isWeb && fontFamilyStyle,
     lineHeightStyle,
     style,
   ])
@@ -87,7 +111,7 @@ export function Text({
       dataSet={Object.assign({tooltip: title}, dataSet || {})}
       selectable={selectable}
       {...props}>
-      {children}
+      {isIOS && emoji ? renderChildrenWithEmoji(children) : children}
     </RNText>
   )
 }
diff --git a/src/view/com/util/text/ThemedText.tsx b/src/view/com/util/text/ThemedText.tsx
deleted file mode 100644
index 2844d273c..000000000
--- a/src/view/com/util/text/ThemedText.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React from 'react'
-import {CustomTextProps, Text} from './Text'
-import {usePalette} from 'lib/hooks/usePalette'
-import {addStyle} from 'lib/styles'
-
-export type ThemedTextProps = CustomTextProps & {
-  fg?: 'default' | 'light' | 'error' | 'inverted' | 'inverted-light'
-  bg?: 'default' | 'light' | 'error' | 'inverted' | 'inverted-light'
-  border?: 'default' | 'dark' | 'error' | 'inverted' | 'inverted-dark'
-  lineHeight?: number
-}
-
-export function ThemedText({
-  fg,
-  bg,
-  border,
-  style,
-  children,
-  ...props
-}: React.PropsWithChildren<ThemedTextProps>) {
-  const pal = usePalette('default')
-  const palInverted = usePalette('inverted')
-  const palError = usePalette('error')
-  switch (fg) {
-    case 'default':
-      style = addStyle(style, pal.text)
-      break
-    case 'light':
-      style = addStyle(style, pal.textLight)
-      break
-    case 'error':
-      style = addStyle(style, {color: palError.colors.background})
-      break
-    case 'inverted':
-      style = addStyle(style, palInverted.text)
-      break
-    case 'inverted-light':
-      style = addStyle(style, palInverted.textLight)
-      break
-  }
-  switch (bg) {
-    case 'default':
-      style = addStyle(style, pal.view)
-      break
-    case 'light':
-      style = addStyle(style, pal.viewLight)
-      break
-    case 'error':
-      style = addStyle(style, palError.view)
-      break
-    case 'inverted':
-      style = addStyle(style, palInverted.view)
-      break
-    case 'inverted-light':
-      style = addStyle(style, palInverted.viewLight)
-      break
-  }
-  switch (border) {
-    case 'default':
-      style = addStyle(style, pal.border)
-      break
-    case 'dark':
-      style = addStyle(style, pal.borderDark)
-      break
-    case 'error':
-      style = addStyle(style, palError.border)
-      break
-    case 'inverted':
-      style = addStyle(style, palInverted.border)
-      break
-    case 'inverted-dark':
-      style = addStyle(style, palInverted.borderDark)
-      break
-  }
-  return (
-    <Text style={style} {...props}>
-      {children}
-    </Text>
-  )
-}