about summary refs log tree commit diff
path: root/src/view/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com')
-rw-r--r--src/view/com/auth/create/CreateAccount.tsx3
-rw-r--r--src/view/com/auth/login/Login.tsx3
-rw-r--r--src/view/com/composer/Composer.tsx41
-rw-r--r--src/view/com/composer/photos/Gallery.tsx244
-rw-r--r--src/view/com/modals/AltImage.tsx167
-rw-r--r--src/view/com/modals/Confirm.tsx16
-rw-r--r--src/view/com/post-thread/PostThread.tsx6
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx2
-rw-r--r--src/view/com/post/Post.tsx6
-rw-r--r--src/view/com/posts/FeedSlice.tsx2
-rw-r--r--src/view/com/util/images/Gallery.tsx19
-rw-r--r--src/view/com/util/post-embeds/index.tsx19
12 files changed, 311 insertions, 217 deletions
diff --git a/src/view/com/auth/create/CreateAccount.tsx b/src/view/com/auth/create/CreateAccount.tsx
index 97200709b..d6cb1a0a7 100644
--- a/src/view/com/auth/create/CreateAccount.tsx
+++ b/src/view/com/auth/create/CreateAccount.tsx
@@ -56,9 +56,10 @@ export const CreateAccount = observer(
       } else {
         try {
           await model.submit()
-          track('Create Account')
         } catch {
           // dont need to handle here
+        } finally {
+          track('Try Create Account')
         }
       }
     }, [model, track])
diff --git a/src/view/com/auth/login/Login.tsx b/src/view/com/auth/login/Login.tsx
index af4f01874..c76c33938 100644
--- a/src/view/com/auth/login/Login.tsx
+++ b/src/view/com/auth/login/Login.tsx
@@ -327,7 +327,6 @@ const LoginForm = ({
         identifier: fullIdent,
         password,
       })
-      track('Sign In', {resumedSession: false})
     } catch (e: any) {
       const errMsg = e.toString()
       store.log.warn('Failed to login', e)
@@ -341,6 +340,8 @@ const LoginForm = ({
       } else {
         setError(cleanError(errMsg))
       }
+    } finally {
+      track('Sign In', {resumedSession: false})
     }
   }
 
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 52b90b6c7..fc324d3e5 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -92,18 +92,24 @@ export const ComposePost = observer(function ComposePost({
 
   const onPressCancel = useCallback(() => {
     if (graphemeLength > 0 || !gallery.isEmpty) {
+      if (store.shell.activeModals.some(modal => modal.name === 'confirm')) {
+        store.shell.closeModal()
+      }
       store.shell.openModal({
         name: 'confirm',
-        title: 'Cancel draft',
-        onPressConfirm: onClose,
+        title: 'Discard draft',
+        onPressConfirm: hackfixOnClose,
         onPressCancel: () => {
           store.shell.closeModal()
         },
-        message: "Are you sure you'd like to cancel this draft?",
+        message: "Are you sure you'd like to discard this draft?",
+        confirmBtnText: 'Discard',
+        confirmBtnStyle: {backgroundColor: colors.red4},
       })
+    } else {
+      hackfixOnClose()
     }
-    hackfixOnClose()
-  }, [store, hackfixOnClose, graphemeLength, gallery, onClose])
+  }, [store, hackfixOnClose, graphemeLength, gallery])
 
   // initial setup
   useEffect(() => {
@@ -114,14 +120,10 @@ export const ComposePost = observer(function ComposePost({
   const onEscape = useCallback(
     (e: KeyboardEvent) => {
       if (e.key === 'Escape') {
-        if (store.shell.activeModals.some(modal => modal.name === 'confirm')) {
-          store.shell.closeModal()
-        }
-
         onPressCancel()
       }
     },
-    [store, onPressCancel],
+    [onPressCancel],
   )
   useEffect(() => {
     if (isDesktopWeb) {
@@ -172,9 +174,6 @@ export const ComposePost = observer(function ComposePost({
           knownHandles: autocompleteView.knownHandles,
           langs: store.preferences.postLanguages,
         })
-        track('Create Post', {
-          imageCount: gallery.size,
-        })
       } catch (e: any) {
         if (extLink) {
           setExtLink({
@@ -186,6 +185,11 @@ export const ComposePost = observer(function ComposePost({
         setError(cleanError(e.message))
         setIsProcessing(false)
         return
+      } finally {
+        track('Create Post', {
+          imageCount: gallery.size,
+        })
+        if (replyTo && replyTo.uri) track('Post:Reply')
       }
       if (!replyTo) {
         await store.me.mainFeed.addPostToTop(createdPost.uri)
@@ -227,13 +231,13 @@ export const ComposePost = observer(function ComposePost({
       <View style={[s.flex1, viewStyles]} aria-modal accessibilityViewIsModal>
         <View style={styles.topbar}>
           <TouchableOpacity
-            testID="composerCancelButton"
+            testID="composerDiscardButton"
             onPress={onPressCancel}
             onAccessibilityEscape={onPressCancel}
             accessibilityRole="button"
-            accessibilityLabel="Cancel"
-            accessibilityHint="Closes post composer">
-            <Text style={[pal.link, s.f18]}>Cancel</Text>
+            accessibilityLabel="Discard"
+            accessibilityHint="Closes post composer and discards post draft">
+            <Text style={[pal.link, s.f18, styles.discard]}>Discard</Text>
           </TouchableOpacity>
           <View style={s.flex1} />
           {isProcessing ? (
@@ -386,6 +390,9 @@ const styles = StyleSheet.create({
     paddingHorizontal: 20,
     height: 55,
   },
+  discard: {
+    color: colors.red3,
+  },
   postBtn: {
     borderRadius: 20,
     paddingHorizontal: 20,
diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx
index f46c05333..c226d25cc 100644
--- a/src/view/com/composer/photos/Gallery.tsx
+++ b/src/view/com/composer/photos/Gallery.tsx
@@ -1,16 +1,16 @@
-import React, {useCallback} from 'react'
+import React from 'react'
 import {ImageStyle, Keyboard} from 'react-native'
 import {GalleryModel} from 'state/models/media/gallery'
 import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {colors} from 'lib/styles'
+import {s, colors} from 'lib/styles'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {ImageModel} from 'state/models/media/image'
 import {Image} from 'expo-image'
 import {Text} from 'view/com/util/text/Text'
 import {isDesktopWeb} from 'platform/detection'
 import {openAltTextModal} from 'lib/media/alt-text'
 import {useStores} from 'state/index'
+import {usePalette} from 'lib/hooks/usePalette'
 
 interface Props {
   gallery: GalleryModel
@@ -18,67 +18,39 @@ interface Props {
 
 export const Gallery = observer(function ({gallery}: Props) {
   const store = useStores()
-  const getImageStyle = useCallback(() => {
-    let side: number
+  const pal = usePalette('default')
 
-    if (gallery.size === 1) {
-      side = 250
-    } else {
-      side = (isDesktopWeb ? 560 : 350) / gallery.size
-    }
+  let side: number
 
-    return {
-      height: side,
-      width: side,
-    }
-  }, [gallery])
-
-  const imageStyle = getImageStyle()
-  const handleAddImageAltText = useCallback(
-    (image: ImageModel) => {
-      Keyboard.dismiss()
-      openAltTextModal(store, image)
-    },
-    [store],
-  )
-  const handleRemovePhoto = useCallback(
-    (image: ImageModel) => {
-      gallery.remove(image)
-    },
-    [gallery],
-  )
+  if (gallery.size === 1) {
+    side = 250
+  } else {
+    side = (isDesktopWeb ? 560 : 350) / gallery.size
+  }
 
-  const handleEditPhoto = useCallback(
-    (image: ImageModel) => {
-      gallery.edit(image)
-    },
-    [gallery],
-  )
+  const imageStyle = {
+    height: side,
+    width: side,
+  }
 
   const isOverflow = !isDesktopWeb && gallery.size > 2
 
-  const imageControlLabelStyle = {
-    borderRadius: 5,
-    paddingHorizontal: 10,
-    position: 'absolute' as const,
-    zIndex: 1,
-    ...(isOverflow
-      ? {
-          left: 4,
-          bottom: 4,
-        }
-      : isDesktopWeb && gallery.size < 3
-      ? {
-          left: 8,
-          top: 8,
-        }
-      : {
-          left: 4,
-          top: 4,
-        }),
-  }
+  const altTextControlStyle = isOverflow
+    ? {
+        left: 4,
+        bottom: 4,
+      }
+    : isDesktopWeb && gallery.size < 3
+    ? {
+        left: 8,
+        top: 8,
+      }
+    : {
+        left: 4,
+        top: 4,
+      }
 
-  const imageControlsSubgroupStyle = {
+  const imageControlsStyle = {
     display: 'flex' as const,
     flexDirection: 'row' as const,
     position: 'absolute' as const,
@@ -103,63 +75,90 @@ export const Gallery = observer(function ({gallery}: Props) {
   }
 
   return !gallery.isEmpty ? (
-    <View testID="selectedPhotosView" style={styles.gallery}>
-      {gallery.images.map(image => (
-        <View key={`selected-image-${image.path}`} style={[imageStyle]}>
-          <TouchableOpacity
-            testID="altTextButton"
-            accessibilityRole="button"
-            accessibilityLabel="Add alt text"
-            accessibilityHint=""
-            onPress={() => {
-              handleAddImageAltText(image)
-            }}
-            style={imageControlLabelStyle}>
-            <Text style={styles.imageControlTextContent}>ALT</Text>
-          </TouchableOpacity>
-          <View style={imageControlsSubgroupStyle}>
+    <>
+      <View testID="selectedPhotosView" style={styles.gallery}>
+        {gallery.images.map(image => (
+          <View key={`selected-image-${image.path}`} style={[imageStyle]}>
             <TouchableOpacity
-              testID="editPhotoButton"
+              testID="altTextButton"
               accessibilityRole="button"
-              accessibilityLabel="Edit image"
+              accessibilityLabel="Add alt text"
               accessibilityHint=""
               onPress={() => {
-                handleEditPhoto(image)
+                Keyboard.dismiss()
+                openAltTextModal(store, image)
               }}
-              style={styles.imageControl}>
-              <FontAwesomeIcon
-                icon="pen"
-                size={12}
-                style={{color: colors.white}}
-              />
+              style={[styles.altTextControl, altTextControlStyle]}>
+              <Text style={styles.altTextControlLabel}>ALT</Text>
+              {image.altText.length > 0 ? (
+                <FontAwesomeIcon
+                  icon="check"
+                  size={10}
+                  style={{color: colors.green3}}
+                />
+              ) : undefined}
             </TouchableOpacity>
+            <View style={imageControlsStyle}>
+              <TouchableOpacity
+                testID="editPhotoButton"
+                accessibilityRole="button"
+                accessibilityLabel="Edit image"
+                accessibilityHint=""
+                onPress={() => gallery.edit(image)}
+                style={styles.imageControl}>
+                <FontAwesomeIcon
+                  icon="pen"
+                  size={12}
+                  style={{color: colors.white}}
+                />
+              </TouchableOpacity>
+              <TouchableOpacity
+                testID="removePhotoButton"
+                accessibilityRole="button"
+                accessibilityLabel="Remove image"
+                accessibilityHint=""
+                onPress={() => gallery.remove(image)}
+                style={styles.imageControl}>
+                <FontAwesomeIcon
+                  icon="xmark"
+                  size={16}
+                  style={{color: colors.white}}
+                />
+              </TouchableOpacity>
+            </View>
             <TouchableOpacity
-              testID="removePhotoButton"
               accessibilityRole="button"
-              accessibilityLabel="Remove image"
+              accessibilityLabel="Add alt text"
               accessibilityHint=""
-              onPress={() => handleRemovePhoto(image)}
-              style={styles.imageControl}>
-              <FontAwesomeIcon
-                icon="xmark"
-                size={16}
-                style={{color: colors.white}}
-              />
-            </TouchableOpacity>
-          </View>
+              onPress={() => {
+                Keyboard.dismiss()
+                openAltTextModal(store, image)
+              }}
+              style={styles.altTextHiddenRegion}
+            />
 
-          <Image
-            testID="selectedPhotoImage"
-            style={[styles.image, imageStyle] as ImageStyle}
-            source={{
-              uri: image.cropped?.path ?? image.path,
-            }}
-            accessible={true}
-            accessibilityIgnoresInvertColors
-          />
+            <Image
+              testID="selectedPhotoImage"
+              style={[styles.image, imageStyle] as ImageStyle}
+              source={{
+                uri: image.cropped?.path ?? image.path,
+              }}
+              accessible={true}
+              accessibilityIgnoresInvertColors
+            />
+          </View>
+        ))}
+      </View>
+      <View style={[styles.reminder]}>
+        <View style={[styles.infoIcon, pal.viewLight]}>
+          <FontAwesomeIcon icon="info" size={12} color={pal.colors.text} />
         </View>
-      ))}
-    </View>
+        <Text type="sm" style={[pal.textLight, s.flex1]}>
+          Alt text describes images for blind and low-vision users, and helps
+          give context to everyone.
+        </Text>
+      </View>
+    </>
   ) : null
 })
 
@@ -179,19 +178,46 @@ const styles = StyleSheet.create({
     height: 24,
     borderRadius: 12,
     backgroundColor: 'rgba(0, 0, 0, 0.75)',
-    borderWidth: 0.5,
     alignItems: 'center',
     justifyContent: 'center',
   },
-  imageControlTextContent: {
+  altTextControl: {
+    position: 'absolute',
+    zIndex: 1,
     borderRadius: 6,
+    backgroundColor: 'rgba(0, 0, 0, 0.75)',
+    paddingHorizontal: 8,
+    paddingVertical: 3,
+    flexDirection: 'row',
+    alignItems: 'center',
+  },
+  altTextControlLabel: {
     color: 'white',
     fontSize: 12,
     fontWeight: 'bold',
     letterSpacing: 1,
-    backgroundColor: 'rgba(0, 0, 0, 0.75)',
-    borderWidth: 0.5,
-    paddingHorizontal: 10,
-    paddingVertical: 3,
+  },
+  altTextHiddenRegion: {
+    position: 'absolute',
+    left: 4,
+    right: 4,
+    bottom: 4,
+    top: 30,
+    zIndex: 1,
+  },
+
+  reminder: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    gap: 8,
+    borderRadius: 8,
+    paddingVertical: 14,
+  },
+  infoIcon: {
+    width: 22,
+    height: 22,
+    borderRadius: 12,
+    alignItems: 'center',
+    justifyContent: 'center',
   },
 })
diff --git a/src/view/com/modals/AltImage.tsx b/src/view/com/modals/AltImage.tsx
index 07270d557..e1145a0fe 100644
--- a/src/view/com/modals/AltImage.tsx
+++ b/src/view/com/modals/AltImage.tsx
@@ -1,5 +1,15 @@
-import React, {useCallback, useState} from 'react'
-import {StyleSheet, TextInput, TouchableOpacity, View} from 'react-native'
+import React, {useMemo, useCallback, useState} from 'react'
+import {
+  ImageStyle,
+  KeyboardAvoidingView,
+  ScrollView,
+  StyleSheet,
+  TextInput,
+  TouchableOpacity,
+  View,
+  useWindowDimensions,
+} from 'react-native'
+import {Image} from 'expo-image'
 import {usePalette} from 'lib/hooks/usePalette'
 import {gradients, s} from 'lib/styles'
 import {enforceLen} from 'lib/strings/helpers'
@@ -8,7 +18,7 @@ import {useTheme} from 'lib/ThemeContext'
 import {Text} from '../util/text/Text'
 import LinearGradient from 'react-native-linear-gradient'
 import {useStores} from 'state/index'
-import {isDesktopWeb} from 'platform/detection'
+import {isDesktopWeb, isAndroid} from 'platform/detection'
 import {ImageModel} from 'state/models/media/image'
 
 export const snapPoints = ['fullscreen']
@@ -22,6 +32,24 @@ export function Component({image}: Props) {
   const store = useStores()
   const theme = useTheme()
   const [altText, setAltText] = useState(image.altText)
+  const windim = useWindowDimensions()
+
+  const imageStyles = useMemo<ImageStyle>(() => {
+    const maxWidth = isDesktopWeb ? 450 : windim.width
+    if (image.height > image.width) {
+      return {
+        resizeMode: 'contain',
+        width: '100%',
+        aspectRatio: 1,
+        borderRadius: 8,
+      }
+    }
+    return {
+      width: '100%',
+      height: (maxWidth / image.width) * image.height,
+      borderRadius: 8,
+    }
+  }, [image, windim])
 
   const onPressSave = useCallback(() => {
     image.setAltText(altText)
@@ -33,69 +61,94 @@ export function Component({image}: Props) {
   }
 
   return (
-    <View
-      testID="altTextImageModal"
-      style={[pal.view, styles.container, s.flex1]}
-      nativeID="imageAltText">
-      <Text style={[styles.title, pal.text]}>Add alt text</Text>
-      <TextInput
-        testID="altTextImageInput"
-        style={[styles.textArea, pal.border, pal.text]}
-        keyboardAppearance={theme.colorScheme}
-        multiline
-        value={altText}
-        onChangeText={text => setAltText(enforceLen(text, MAX_ALT_TEXT))}
-        accessibilityLabel="Image alt text"
-        accessibilityHint="Sets image alt text for screenreaders"
-        accessibilityLabelledBy="imageAltText"
-      />
-      <View style={styles.buttonControls}>
-        <TouchableOpacity
-          testID="altTextImageSaveBtn"
-          onPress={onPressSave}
-          accessibilityLabel="Save alt text"
-          accessibilityHint={`Saves alt text, which reads: ${altText}`}
-          accessibilityRole="button">
-          <LinearGradient
-            colors={[gradients.blueLight.start, gradients.blueLight.end]}
-            start={{x: 0, y: 0}}
-            end={{x: 1, y: 1}}
-            style={[styles.button]}>
-            <Text type="button-lg" style={[s.white, s.bold]}>
-              Save
-            </Text>
-          </LinearGradient>
-        </TouchableOpacity>
-        <TouchableOpacity
-          testID="altTextImageCancelBtn"
-          onPress={onPressCancel}
-          accessibilityRole="button"
-          accessibilityLabel="Cancel add image alt text"
-          accessibilityHint="Exits adding alt text to image"
-          onAccessibilityEscape={onPressCancel}>
-          <View style={[styles.button]}>
-            <Text type="button-lg" style={[pal.textLight]}>
-              Cancel
-            </Text>
+    <KeyboardAvoidingView
+      behavior={isAndroid ? 'height' : 'padding'}
+      style={[pal.view, styles.container]}>
+      <ScrollView
+        testID="altTextImageModal"
+        style={styles.scrollContainer}
+        keyboardShouldPersistTaps="always"
+        nativeID="imageAltText">
+        <View style={styles.scrollInner}>
+          <View style={[pal.viewLight, styles.imageContainer]}>
+            <Image
+              testID="selectedPhotoImage"
+              style={imageStyles}
+              source={{
+                uri: image.cropped?.path ?? image.path,
+              }}
+              accessible={true}
+              accessibilityIgnoresInvertColors
+            />
+          </View>
+          <TextInput
+            testID="altTextImageInput"
+            style={[styles.textArea, pal.border, pal.text]}
+            keyboardAppearance={theme.colorScheme}
+            multiline
+            placeholder="Add alt text"
+            placeholderTextColor={pal.colors.textLight}
+            value={altText}
+            onChangeText={text => setAltText(enforceLen(text, MAX_ALT_TEXT))}
+            accessibilityLabel="Image alt text"
+            accessibilityHint=""
+            accessibilityLabelledBy="imageAltText"
+            autoFocus
+          />
+          <View style={styles.buttonControls}>
+            <TouchableOpacity
+              testID="altTextImageSaveBtn"
+              onPress={onPressSave}
+              accessibilityLabel="Save alt text"
+              accessibilityHint={`Saves alt text, which reads: ${altText}`}
+              accessibilityRole="button">
+              <LinearGradient
+                colors={[gradients.blueLight.start, gradients.blueLight.end]}
+                start={{x: 0, y: 0}}
+                end={{x: 1, y: 1}}
+                style={[styles.button]}>
+                <Text type="button-lg" style={[s.white, s.bold]}>
+                  Save
+                </Text>
+              </LinearGradient>
+            </TouchableOpacity>
+            <TouchableOpacity
+              testID="altTextImageCancelBtn"
+              onPress={onPressCancel}
+              accessibilityRole="button"
+              accessibilityLabel="Cancel add image alt text"
+              accessibilityHint=""
+              onAccessibilityEscape={onPressCancel}>
+              <View style={[styles.button]}>
+                <Text type="button-lg" style={[pal.textLight]}>
+                  Cancel
+                </Text>
+              </View>
+            </TouchableOpacity>
           </View>
-        </TouchableOpacity>
-      </View>
-    </View>
+        </View>
+      </ScrollView>
+    </KeyboardAvoidingView>
   )
 }
 
 const styles = StyleSheet.create({
   container: {
-    gap: 18,
-    paddingVertical: isDesktopWeb ? 0 : 18,
-    paddingHorizontal: isDesktopWeb ? 0 : 12,
+    flex: 1,
     height: '100%',
     width: '100%',
+    paddingVertical: isDesktopWeb ? 0 : 18,
+  },
+  scrollContainer: {
+    flex: 1,
+    height: '100%',
+    paddingHorizontal: isDesktopWeb ? 0 : 12,
+  },
+  scrollInner: {
+    gap: 12,
   },
-  title: {
-    textAlign: 'center',
-    fontWeight: 'bold',
-    fontSize: 24,
+  imageContainer: {
+    borderRadius: 8,
   },
   textArea: {
     borderWidth: 1,
diff --git a/src/view/com/modals/Confirm.tsx b/src/view/com/modals/Confirm.tsx
index 11e1a6334..f9bc0de14 100644
--- a/src/view/com/modals/Confirm.tsx
+++ b/src/view/com/modals/Confirm.tsx
@@ -12,6 +12,7 @@ import {ErrorMessage} from '../util/error/ErrorMessage'
 import {cleanError} from 'lib/strings/errors'
 import {usePalette} from 'lib/hooks/usePalette'
 import {isDesktopWeb} from 'platform/detection'
+import type {ConfirmModal} from 'state/models/ui/shell'
 
 export const snapPoints = ['50%']
 
@@ -20,12 +21,9 @@ export function Component({
   message,
   onPressConfirm,
   onPressCancel,
-}: {
-  title: string
-  message: string | (() => JSX.Element)
-  onPressConfirm: () => void | Promise<void>
-  onPressCancel?: () => void | Promise<void>
-}) {
+  confirmBtnText,
+  confirmBtnStyle,
+}: ConfirmModal) {
   const pal = usePalette('default')
   const store = useStores()
   const [isProcessing, setIsProcessing] = useState<boolean>(false)
@@ -68,11 +66,13 @@ export function Component({
         <TouchableOpacity
           testID="confirmBtn"
           onPress={onPress}
-          style={[styles.btn]}
+          style={[styles.btn, confirmBtnStyle]}
           accessibilityRole="button"
           accessibilityLabel="Confirm"
           accessibilityHint="">
-          <Text style={[s.white, s.bold, s.f18]}>Confirm</Text>
+          <Text style={[s.white, s.bold, s.f18]}>
+            {confirmBtnText ?? 'Confirm'}
+          </Text>
         </TouchableOpacity>
       )}
       {onPressCancel === undefined ? null : (
diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx
index 610b96507..51f63dbb3 100644
--- a/src/view/com/post-thread/PostThread.tsx
+++ b/src/view/com/post-thread/PostThread.tsx
@@ -9,10 +9,8 @@ import {
 } from 'react-native'
 import {AppBskyFeedDefs} from '@atproto/api'
 import {CenteredView, FlatList} from '../util/Views'
-import {
-  PostThreadModel,
-  PostThreadItemModel,
-} from 'state/models/content/post-thread'
+import {PostThreadModel} from 'state/models/content/post-thread'
+import {PostThreadItemModel} from 'state/models/content/post-thread-item'
 import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 647468401..002795d77 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -7,7 +7,7 @@ import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
-import {PostThreadItemModel} from 'state/models/content/post-thread'
+import {PostThreadItemModel} from 'state/models/content/post-thread-item'
 import {Link} from '../util/Link'
 import {RichText} from '../util/text/RichText'
 import {Text} from '../util/text/Text'
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index b9d146dee..3eac7ee7b 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -13,10 +13,8 @@ import {observer} from 'mobx-react-lite'
 import Clipboard from '@react-native-clipboard/clipboard'
 import {AtUri} from '@atproto/api'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {
-  PostThreadModel,
-  PostThreadItemModel,
-} from 'state/models/content/post-thread'
+import {PostThreadModel} from 'state/models/content/post-thread'
+import {PostThreadItemModel} from 'state/models/content/post-thread-item'
 import {Link} from '../util/Link'
 import {UserInfoText} from '../util/UserInfoText'
 import {PostMeta} from '../util/PostMeta'
diff --git a/src/view/com/posts/FeedSlice.tsx b/src/view/com/posts/FeedSlice.tsx
index 888466200..d75ff1385 100644
--- a/src/view/com/posts/FeedSlice.tsx
+++ b/src/view/com/posts/FeedSlice.tsx
@@ -1,6 +1,6 @@
 import React from 'react'
 import {StyleSheet, View} from 'react-native'
-import {PostsFeedSliceModel} from 'state/models/feeds/post'
+import {PostsFeedSliceModel} from 'state/models/feeds/posts-slice'
 import {AtUri} from '@atproto/api'
 import {Link} from '../util/Link'
 import {Text} from '../util/text/Text'
diff --git a/src/view/com/util/images/Gallery.tsx b/src/view/com/util/images/Gallery.tsx
index 723db289c..a7a64b171 100644
--- a/src/view/com/util/images/Gallery.tsx
+++ b/src/view/com/util/images/Gallery.tsx
@@ -45,23 +45,28 @@ export const GalleryItem: FC<GalleryItemProps> = ({
           accessibilityIgnoresInvertColors
         />
       </TouchableOpacity>
-      {image.alt === '' ? null : <Text style={styles.alt}>ALT</Text>}
+      {image.alt === '' ? null : (
+        <View style={styles.altContainer}>
+          <Text style={styles.alt}>ALT</Text>
+        </View>
+      )}
     </View>
   )
 }
 
 const styles = StyleSheet.create({
-  alt: {
+  altContainer: {
     backgroundColor: 'rgba(0, 0, 0, 0.75)',
     borderRadius: 6,
-    color: 'white',
-    fontSize: 12,
-    fontWeight: 'bold',
-    letterSpacing: 1,
-    paddingHorizontal: 10,
+    paddingHorizontal: 6,
     paddingVertical: 3,
     position: 'absolute',
     left: 6,
     bottom: 6,
   },
+  alt: {
+    color: 'white',
+    fontSize: 10,
+    fontWeight: 'bold',
+  },
 })
diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx
index 53ef17318..7f2244b7b 100644
--- a/src/view/com/util/post-embeds/index.tsx
+++ b/src/view/com/util/post-embeds/index.tsx
@@ -126,7 +126,11 @@ export function PostEmbeds({
               onPress={() => openLightbox(0)}
               onPressIn={() => onPressIn(0)}
               style={styles.singleImage}>
-              {alt === '' ? null : <Text style={styles.alt}>ALT</Text>}
+              {alt === '' ? null : (
+                <View style={styles.altContainer}>
+                  <Text style={styles.alt}>ALT</Text>
+                </View>
+              )}
             </AutoSizedImage>
           </View>
         )
@@ -201,17 +205,18 @@ const styles = StyleSheet.create({
     borderRadius: 8,
     marginTop: 4,
   },
-  alt: {
+  altContainer: {
     backgroundColor: 'rgba(0, 0, 0, 0.75)',
     borderRadius: 6,
-    color: 'white',
-    fontSize: 12,
-    fontWeight: 'bold',
-    letterSpacing: 1,
-    paddingHorizontal: 10,
+    paddingHorizontal: 6,
     paddingVertical: 3,
     position: 'absolute',
     left: 6,
     bottom: 6,
   },
+  alt: {
+    color: 'white',
+    fontSize: 10,
+    fontWeight: 'bold',
+  },
 })