about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSnackpackWayne <49062420+snackpackwayne@users.noreply.github.com>2023-12-27 22:38:01 +0100
committerGitHub <noreply@github.com>2023-12-27 22:38:01 +0100
commit52cb777c9432354302b3285e141aa96632af34d5 (patch)
tree0b823b5816f040990af0ee2fc7815a2f1a063fca /src
parent538c67cc332a6b909f83dd382843667dcced0fe9 (diff)
parentf402f33a024ea59ea9bd2decfc0444e6836a934c (diff)
downloadvoidsky-52cb777c9432354302b3285e141aa96632af34d5.tar.zst
Merge branch 'main' into patch-1
Diffstat (limited to 'src')
-rw-r--r--src/lib/ThemeContext.tsx32
-rw-r--r--src/lib/api/feed-manip.ts19
-rw-r--r--src/locale/i18n.ts2
-rw-r--r--src/locale/locales/de/messages.po32
-rw-r--r--src/locale/locales/en/messages.po22
-rw-r--r--src/locale/locales/es/messages.po22
-rw-r--r--src/locale/locales/fr/messages.po22
-rw-r--r--src/locale/locales/hi/messages.po22
-rw-r--r--src/locale/locales/ja/messages.po62
-rw-r--r--src/state/queries/notifications/unread.tsx3
-rw-r--r--src/state/queries/notifications/util.ts4
-rw-r--r--src/view/com/composer/Composer.tsx8
-rw-r--r--src/view/com/modals/Threadgate.tsx4
-rw-r--r--src/view/com/notifications/FeedItem.tsx18
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx7
-rw-r--r--src/view/com/posts/FeedItem.tsx14
-rw-r--r--src/view/com/util/PostMeta.tsx4
-rw-r--r--src/view/com/util/post-ctrls/PostCtrls.tsx3
-rw-r--r--src/view/screens/Notifications.tsx8
-rw-r--r--src/view/screens/PreferencesHomeFeed.tsx2
-rw-r--r--src/view/screens/ProfileFeed.tsx9
-rw-r--r--src/view/screens/ProfileList.tsx9
-rw-r--r--src/view/screens/Search/Search.tsx5
23 files changed, 213 insertions, 120 deletions
diff --git a/src/lib/ThemeContext.tsx b/src/lib/ThemeContext.tsx
index 483c50c42..38bd199cb 100644
--- a/src/lib/ThemeContext.tsx
+++ b/src/lib/ThemeContext.tsx
@@ -1,9 +1,7 @@
-import {isWeb} from 'platform/detection'
 import React, {ReactNode, createContext, useContext} from 'react'
 import {
-  AppState,
   TextStyle,
-  useColorScheme as useColorScheme_BUGGY,
+  useColorScheme,
   ViewStyle,
   ColorSchemeName,
 } from 'react-native'
@@ -97,37 +95,11 @@ function getTheme(theme: ColorSchemeName) {
   return theme === 'dark' ? darkTheme : defaultTheme
 }
 
-/**
- * With RN iOS, we can only "trust" the color scheme reported while the app is
- * active. This is a workaround until the bug is fixed upstream.
- *
- * @see https://github.com/bluesky-social/social-app/pull/1417#issuecomment-1719868504
- * @see https://github.com/facebook/react-native/pull/39439
- */
-function useColorScheme_FIXED() {
-  const colorScheme = useColorScheme_BUGGY()
-  const [currentColorScheme, setCurrentColorScheme] =
-    React.useState<ColorSchemeName>(colorScheme)
-
-  React.useEffect(() => {
-    // we don't need to be updating state on web
-    if (isWeb) return
-    const subscription = AppState.addEventListener('change', state => {
-      const isActive = state === 'active'
-      if (!isActive) return
-      setCurrentColorScheme(colorScheme)
-    })
-    return () => subscription.remove()
-  }, [colorScheme])
-
-  return isWeb ? colorScheme : currentColorScheme
-}
-
 export const ThemeProvider: React.FC<ThemeProviderProps> = ({
   theme,
   children,
 }) => {
-  const colorScheme = useColorScheme_FIXED()
+  const colorScheme = useColorScheme()
   const themeValue = getTheme(theme === 'system' ? colorScheme : theme)
 
   return (
diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts
index 9a050fd3e..c964693c4 100644
--- a/src/lib/api/feed-manip.ts
+++ b/src/lib/api/feed-manip.ts
@@ -117,11 +117,7 @@ export class FeedViewPostsSlice {
 }
 
 export class NoopFeedTuner {
-  private keyCounter = 0
-
-  reset() {
-    this.keyCounter = 0
-  }
+  reset() {}
   tune(
     feed: FeedViewPost[],
     _opts?: {dryRun: boolean; maintainOrder: boolean},
@@ -131,13 +127,13 @@ export class NoopFeedTuner {
 }
 
 export class FeedTuner {
-  private keyCounter = 0
+  seenKeys: Set<string> = new Set()
   seenUris: Set<string> = new Set()
 
   constructor(public tunerFns: FeedTunerFn[]) {}
 
   reset() {
-    this.keyCounter = 0
+    this.seenKeys.clear()
     this.seenUris.clear()
   }
 
@@ -218,11 +214,16 @@ export class FeedTuner {
     }
 
     if (!dryRun) {
-      for (const slice of slices) {
+      slices = slices.filter(slice => {
+        if (this.seenKeys.has(slice._reactKey)) {
+          return false
+        }
         for (const item of slice.items) {
           this.seenUris.add(item.post.uri)
         }
-      }
+        this.seenKeys.add(slice._reactKey)
+        return true
+      })
     }
 
     return slices
diff --git a/src/locale/i18n.ts b/src/locale/i18n.ts
index 54762eb81..6f0a46888 100644
--- a/src/locale/i18n.ts
+++ b/src/locale/i18n.ts
@@ -7,7 +7,7 @@ import {messages as messagesHi} from '#/locale/locales/hi/messages'
 import {messages as messagesJa} from '#/locale/locales/ja/messages'
 import {messages as messagesFr} from '#/locale/locales/fr/messages'
 import {messages as messagesDe} from '#/locale/locales/de/messages'
-import {messages as messagesEs} from '#/locale/locales/de/messages'
+import {messages as messagesEs} from '#/locale/locales/es/messages'
 
 import {sanitizeAppLanguageSetting} from '#/locale/helpers'
 import {AppLanguage} from '#/locale/languages'
diff --git a/src/locale/locales/de/messages.po b/src/locale/locales/de/messages.po
index 3bbbf8d50..b7c2ba44e 100644
--- a/src/locale/locales/de/messages.po
+++ b/src/locale/locales/de/messages.po
@@ -105,11 +105,11 @@ msgstr "Details hinzufügen"
 msgid "Add details to report"
 msgstr "Details zum Report hinzufügen"
 
-#: src/view/com/composer/Composer.tsx:438
+#: src/view/com/composer/Composer.tsx:442
 msgid "Add link card"
 msgstr "Link-Karte hinzufügen"
 
-#: src/view/com/composer/Composer.tsx:441
+#: src/view/com/composer/Composer.tsx:445
 msgid "Add link card:"
 msgstr "Link-Karte hinzufügen:"
 
@@ -335,18 +335,22 @@ msgstr "Kamera"
 msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long."
 msgstr "Darf nur Buchstaben, Zahlen, Leerzeichen, Bindestriche und Unterstriche enthalten. Muss mindestens 4 Zeichen lang sein, darf aber nicht länger als 32 Zeichen sein."
 
-#: src/view/com/composer/Composer.tsx:285
-#: src/view/com/composer/Composer.tsx:288 src/view/com/modals/AltImage.tsx:128
+#: src/view/com/composer/Composer.tsx:289
+#: src/view/com/composer/Composer.tsx:292
+#: src/view/com/modals/AltImage.tsx:128
 #: src/view/com/modals/ChangeEmail.tsx:218
-#: src/view/com/modals/ChangeEmail.tsx:220 src/view/com/modals/Confirm.tsx:88
+#: src/view/com/modals/ChangeEmail.tsx:220
+#: src/view/com/modals/Confirm.tsx:88
 #: src/view/com/modals/CreateOrEditList.tsx:267
 #: src/view/com/modals/CreateOrEditList.tsx:272
 #: src/view/com/modals/DeleteAccount.tsx:150
 #: src/view/com/modals/DeleteAccount.tsx:223
 #: src/view/com/modals/EditImage.tsx:323
 #: src/view/com/modals/EditProfile.tsx:248
-#: src/view/com/modals/LinkWarning.tsx:85 src/view/com/modals/Repost.tsx:73
-#: src/view/com/modals/Waitlist.tsx:136 src/view/screens/Search/Search.tsx:584
+#: src/view/com/modals/LinkWarning.tsx:85
+#: src/view/com/modals/Repost.tsx:73
+#: src/view/com/modals/Waitlist.tsx:136
+#: src/view/screens/Search/Search.tsx:584
 #: src/view/shell/desktop/Search.tsx:182
 msgid "Cancel"
 msgstr "Abbrechen"
@@ -651,6 +655,10 @@ msgstr "Entwicklungsserver"
 msgid "Developer Tools"
 msgstr "Entwickler-Tools"
 
+#: src/view/com/composer/Composer.tsx:210
+msgid "Did you want to say anything?"
+msgstr ""
+
 #: src/view/com/composer/Composer.tsx:143
 msgid "Discard"
 msgstr "Verwerfen"
@@ -1346,7 +1354,7 @@ msgstr "Oh nein!"
 msgid "Okay"
 msgstr "Okay"
 
-#: src/view/com/composer/Composer.tsx:354
+#: src/view/com/composer/Composer.tsx:358
 msgid "One or more images is missing alt text."
 msgstr "Bei einem oder mehreren Bildern fehlt der Alt-Text."
 
@@ -1467,7 +1475,11 @@ msgstr "Bitte gib auch dein Passwort ein:"
 msgid "Please tell us why you think this content warning was incorrectly applied!"
 msgstr "Bitte teile uns mit, warum du denkst, dass diese Inhaltswarnung falsch angewendet wurde!"
 
-#: src/view/com/composer/Composer.tsx:337
+#: src/view/com/composer/Composer.tsx:214
+msgid "Please wait for your link card to finish loading"
+msgstr ""
+
+#: src/view/com/composer/Composer.tsx:341
 #: src/view/com/post-thread/PostThread.tsx:225
 #: src/view/screens/PostThread.tsx:80
 msgid "Post"
@@ -2274,7 +2286,7 @@ msgstr "Wer antworten kann"
 msgid "Wide"
 msgstr "Breit"
 
-#: src/view/com/composer/Composer.tsx:409
+#: src/view/com/composer/Composer.tsx:413
 msgid "Write post"
 msgstr "Beitrag verfassen"
 
diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po
index 063b459b4..7139489a9 100644
--- a/src/locale/locales/en/messages.po
+++ b/src/locale/locales/en/messages.po
@@ -130,11 +130,11 @@ msgstr ""
 msgid "Add details to report"
 msgstr ""
 
-#: src/view/com/composer/Composer.tsx:438
+#: src/view/com/composer/Composer.tsx:442
 msgid "Add link card"
 msgstr ""
 
-#: src/view/com/composer/Composer.tsx:441
+#: src/view/com/composer/Composer.tsx:445
 msgid "Add link card:"
 msgstr ""
 
@@ -373,8 +373,8 @@ msgstr ""
 msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long."
 msgstr ""
 
-#: src/view/com/composer/Composer.tsx:285
-#: src/view/com/composer/Composer.tsx:288
+#: src/view/com/composer/Composer.tsx:289
+#: src/view/com/composer/Composer.tsx:292
 #: src/view/com/modals/AltImage.tsx:128
 #: src/view/com/modals/ChangeEmail.tsx:218
 #: src/view/com/modals/ChangeEmail.tsx:220
@@ -705,6 +705,10 @@ msgstr ""
 msgid "Developer Tools"
 msgstr ""
 
+#: src/view/com/composer/Composer.tsx:210
+msgid "Did you want to say anything?"
+msgstr ""
+
 #: src/view/com/composer/Composer.tsx:143
 msgid "Discard"
 msgstr ""
@@ -1492,7 +1496,7 @@ msgstr ""
 msgid "Okay"
 msgstr ""
 
-#: src/view/com/composer/Composer.tsx:354
+#: src/view/com/composer/Composer.tsx:358
 msgid "One or more images is missing alt text."
 msgstr ""
 
@@ -1620,7 +1624,11 @@ msgstr ""
 #~ msgid "Please tell us why you think this decision was incorrect."
 #~ msgstr ""
 
-#: src/view/com/composer/Composer.tsx:337
+#: src/view/com/composer/Composer.tsx:214
+msgid "Please wait for your link card to finish loading"
+msgstr ""
+
+#: src/view/com/composer/Composer.tsx:341
 #: src/view/com/post-thread/PostThread.tsx:225
 #: src/view/screens/PostThread.tsx:80
 msgid "Post"
@@ -2497,7 +2505,7 @@ msgstr ""
 msgid "Wide"
 msgstr ""
 
-#: src/view/com/composer/Composer.tsx:409
+#: src/view/com/composer/Composer.tsx:413
 msgid "Write post"
 msgstr ""
 
diff --git a/src/locale/locales/es/messages.po b/src/locale/locales/es/messages.po
index 671e9e229..d1ea8ece8 100644
--- a/src/locale/locales/es/messages.po
+++ b/src/locale/locales/es/messages.po
@@ -110,11 +110,11 @@ msgstr "Agregar detalles"
 msgid "Add details to report"
 msgstr "Agregar detalles al informe"
 
-#: src/view/com/composer/Composer.tsx:438
+#: src/view/com/composer/Composer.tsx:442
 msgid "Add link card"
 msgstr "Agregar una tarjeta de enlace"
 
-#: src/view/com/composer/Composer.tsx:441
+#: src/view/com/composer/Composer.tsx:445
 msgid "Add link card:"
 msgstr "Agregar una tarjeta de enlace:"
 
@@ -345,8 +345,8 @@ msgstr "Cámara"
 msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long."
 msgstr "Sólo puede contener letras, números, espacios, guiones y guiones bajos. Debe tener al menos 4 caracteres, pero no más de 32."
 
-#: src/view/com/composer/Composer.tsx:285
-#: src/view/com/composer/Composer.tsx:288
+#: src/view/com/composer/Composer.tsx:289
+#: src/view/com/composer/Composer.tsx:292
 #: src/view/com/modals/AltImage.tsx:128
 #: src/view/com/modals/ChangeEmail.tsx:218
 #: src/view/com/modals/ChangeEmail.tsx:220
@@ -669,6 +669,10 @@ msgstr "Servidor de desarrollo"
 msgid "Developer Tools"
 msgstr "Herramientas de desarrollador"
 
+#: src/view/com/composer/Composer.tsx:210
+msgid "Did you want to say anything?"
+msgstr ""
+
 #: src/view/com/composer/Composer.tsx:143
 msgid "Discard"
 msgstr "Descartar"
@@ -1398,7 +1402,7 @@ msgstr "¡Qué problema!"
 msgid "Okay"
 msgstr "Está bien"
 
-#: src/view/com/composer/Composer.tsx:354
+#: src/view/com/composer/Composer.tsx:358
 msgid "One or more images is missing alt text."
 msgstr "Falta el texto alternativo en una o varias imágenes."
 
@@ -1526,7 +1530,11 @@ msgstr ""
 #~ msgid "Please tell us why you think this decision was incorrect."
 #~ msgstr "Por favor, dinos por qué crees que esta decisión fue incorrecta."
 
-#: src/view/com/composer/Composer.tsx:337
+#: src/view/com/composer/Composer.tsx:214
+msgid "Please wait for your link card to finish loading"
+msgstr ""
+
+#: src/view/com/composer/Composer.tsx:341
 #: src/view/com/post-thread/PostThread.tsx:225
 #: src/view/screens/PostThread.tsx:80
 msgid "Post"
@@ -2358,7 +2366,7 @@ msgstr "Quién puede responder"
 msgid "Wide"
 msgstr "Ancho"
 
-#: src/view/com/composer/Composer.tsx:409
+#: src/view/com/composer/Composer.tsx:413
 msgid "Write post"
 msgstr "Redactar una publicación"
 
diff --git a/src/locale/locales/fr/messages.po b/src/locale/locales/fr/messages.po
index ca8c6af2b..8afd0e5fe 100644
--- a/src/locale/locales/fr/messages.po
+++ b/src/locale/locales/fr/messages.po
@@ -110,11 +110,11 @@ msgstr "Ajouter des détails"
 msgid "Add details to report"
 msgstr "Ajouter des détails au rapport"
 
-#: src/view/com/composer/Composer.tsx:438
+#: src/view/com/composer/Composer.tsx:442
 msgid "Add link card"
 msgstr "Ajouter une carte link card"
 
-#: src/view/com/composer/Composer.tsx:441
+#: src/view/com/composer/Composer.tsx:445
 msgid "Add link card:"
 msgstr "Ajouter une carte link card :"
 
@@ -345,8 +345,8 @@ msgstr "Caméra"
 msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long."
 msgstr "Ne peut contenir que des lettres, des chiffres, des espaces, des tirets et des tirets bas. La longueur doit être d’au moins 4 caractères, mais pas plus de 32."
 
-#: src/view/com/composer/Composer.tsx:285
-#: src/view/com/composer/Composer.tsx:288
+#: src/view/com/composer/Composer.tsx:289
+#: src/view/com/composer/Composer.tsx:292
 #: src/view/com/modals/AltImage.tsx:128
 #: src/view/com/modals/ChangeEmail.tsx:218
 #: src/view/com/modals/ChangeEmail.tsx:220
@@ -669,6 +669,10 @@ msgstr "Serveur de dév"
 msgid "Developer Tools"
 msgstr "Outils du dév"
 
+#: src/view/com/composer/Composer.tsx:210
+msgid "Did you want to say anything?"
+msgstr ""
+
 #: src/view/com/composer/Composer.tsx:143
 msgid "Discard"
 msgstr "Ignorer"
@@ -1398,7 +1402,7 @@ msgstr "Oh non !"
 msgid "Okay"
 msgstr "D’accord"
 
-#: src/view/com/composer/Composer.tsx:354
+#: src/view/com/composer/Composer.tsx:358
 msgid "One or more images is missing alt text."
 msgstr "Une ou plusieurs images n’ont pas de texte alt."
 
@@ -1526,7 +1530,11 @@ msgstr ""
 #~ msgid "Please tell us why you think this decision was incorrect."
 #~ msgstr "Dites-nous pourquoi vous pensez que cette décision était incorrecte."
 
-#: src/view/com/composer/Composer.tsx:337
+#: src/view/com/composer/Composer.tsx:214
+msgid "Please wait for your link card to finish loading"
+msgstr ""
+
+#: src/view/com/composer/Composer.tsx:341
 #: src/view/com/post-thread/PostThread.tsx:225
 #: src/view/screens/PostThread.tsx:80
 msgid "Post"
@@ -2358,7 +2366,7 @@ msgstr "Qui peut répondre ?"
 msgid "Wide"
 msgstr "Large"
 
-#: src/view/com/composer/Composer.tsx:409
+#: src/view/com/composer/Composer.tsx:413
 msgid "Write post"
 msgstr "Rédiger une publication"
 
diff --git a/src/locale/locales/hi/messages.po b/src/locale/locales/hi/messages.po
index 93769c2ba..6fa32b369 100644
--- a/src/locale/locales/hi/messages.po
+++ b/src/locale/locales/hi/messages.po
@@ -130,11 +130,11 @@ msgstr "विवरण जोड़ें"
 msgid "Add details to report"
 msgstr "रिपोर्ट करने के लिए विवरण जोड़ें"
 
-#: src/view/com/composer/Composer.tsx:438
+#: src/view/com/composer/Composer.tsx:442
 msgid "Add link card"
 msgstr "लिंक कार्ड जोड़ें"
 
-#: src/view/com/composer/Composer.tsx:441
+#: src/view/com/composer/Composer.tsx:445
 msgid "Add link card:"
 msgstr "लिंक कार्ड जोड़ें:"
 
@@ -373,8 +373,8 @@ msgstr "कैमरा"
 msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long."
 msgstr "केवल अक्षर, संख्या, रिक्त स्थान, डैश और अंडरस्कोर हो सकते हैं। कम से कम 4 अक्षर लंबा होना चाहिए, लेकिन 32 अक्षरों से अधिक लंबा नहीं होना चाहिए।।"
 
-#: src/view/com/composer/Composer.tsx:285
-#: src/view/com/composer/Composer.tsx:288
+#: src/view/com/composer/Composer.tsx:289
+#: src/view/com/composer/Composer.tsx:292
 #: src/view/com/modals/AltImage.tsx:128
 #: src/view/com/modals/ChangeEmail.tsx:218
 #: src/view/com/modals/ChangeEmail.tsx:220
@@ -701,6 +701,10 @@ msgstr "देव सर्वर"
 msgid "Developer Tools"
 msgstr "डेवलपर उपकरण"
 
+#: src/view/com/composer/Composer.tsx:210
+msgid "Did you want to say anything?"
+msgstr ""
+
 #: src/view/com/composer/Composer.tsx:143
 msgid "Discard"
 msgstr ""
@@ -1484,7 +1488,7 @@ msgstr "अरे नहीं!"
 msgid "Okay"
 msgstr "ठीक है"
 
-#: src/view/com/composer/Composer.tsx:354
+#: src/view/com/composer/Composer.tsx:358
 msgid "One or more images is missing alt text."
 msgstr "एक या अधिक छवियाँ alt पाठ याद आती हैं।।"
 
@@ -1612,7 +1616,11 @@ msgstr ""
 #~ msgid "Please tell us why you think this decision was incorrect."
 #~ msgstr ""
 
-#: src/view/com/composer/Composer.tsx:337
+#: src/view/com/composer/Composer.tsx:214
+msgid "Please wait for your link card to finish loading"
+msgstr ""
+
+#: src/view/com/composer/Composer.tsx:341
 #: src/view/com/post-thread/PostThread.tsx:225
 #: src/view/screens/PostThread.tsx:80
 msgid "Post"
@@ -2489,7 +2497,7 @@ msgstr ""
 msgid "Wide"
 msgstr "चौड़ा"
 
-#: src/view/com/composer/Composer.tsx:409
+#: src/view/com/composer/Composer.tsx:413
 msgid "Write post"
 msgstr "पोस्ट लिखो"
 
diff --git a/src/locale/locales/ja/messages.po b/src/locale/locales/ja/messages.po
index 685554ae5..d4b2ce3bd 100644
--- a/src/locale/locales/ja/messages.po
+++ b/src/locale/locales/ja/messages.po
@@ -9,7 +9,7 @@ msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
 "PO-Revision-Date: \n"
-"Last-Translator: Hima-Zinn\n"
+"Last-Translator: noritada\n"
 "Language-Team: Hima-Zinn, tkusano, dolciss, oboenikui, noritada\n"
 "Plural-Forms: \n"
 
@@ -110,11 +110,11 @@ msgstr "詳細を追加"
 msgid "Add details to report"
 msgstr "レポートに詳細を追加"
 
-#: src/view/com/composer/Composer.tsx:438
+#: src/view/com/composer/Composer.tsx:442
 msgid "Add link card"
 msgstr "リンクカードを追加"
 
-#: src/view/com/composer/Composer.tsx:441
+#: src/view/com/composer/Composer.tsx:445
 msgid "Add link card:"
 msgstr "リンクカードを追加:"
 
@@ -345,8 +345,8 @@ msgstr "カメラ"
 msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long."
 msgstr "文字、数字、スペース、ハイフン、およびアンダースコアのみが使用可能です。長さは4文字以上32文字以下である必要があります。"
 
-#: src/view/com/composer/Composer.tsx:285
-#: src/view/com/composer/Composer.tsx:288
+#: src/view/com/composer/Composer.tsx:289
+#: src/view/com/composer/Composer.tsx:292
 #: src/view/com/modals/AltImage.tsx:128
 #: src/view/com/modals/ChangeEmail.tsx:218
 #: src/view/com/modals/ChangeEmail.tsx:220
@@ -669,6 +669,10 @@ msgstr "開発者サーバー"
 msgid "Developer Tools"
 msgstr "開発者ツール"
 
+#: src/view/com/composer/Composer.tsx:210
+msgid "Did you want to say anything?"
+msgstr ""
+
 #: src/view/com/composer/Composer.tsx:143
 msgid "Discard"
 msgstr "破棄"
@@ -771,7 +775,7 @@ msgstr "メールアドレス:"
 
 #: src/view/screens/PreferencesHomeFeed.tsx:138
 msgid "Enable this setting to only see replies between people you follow."
-msgstr "この設定を有効にすると、フォローしているユーザー間の返信だけが表示されます。"
+msgstr "この設定を有効にすると、自分がフォローしているユーザーからの返信だけが表示されます。"
 
 #: src/view/screens/Profile.tsx:426
 msgid "End of feed"
@@ -878,11 +882,11 @@ msgstr "何人かのユーザーをフォローして開始します。興味を
 
 #: src/view/com/modals/Threadgate.tsx:98
 msgid "Followed users"
-msgstr "フォローしているユーザー"
+msgstr "自分がフォローしているユーザー"
 
 #: src/view/screens/PreferencesHomeFeed.tsx:145
 msgid "Followed users only"
-msgstr "フォローされたユーザーのみ"
+msgstr "自分がフォローしているユーザーのみ"
 
 #: src/view/screens/ProfileFollowers.tsx:25
 msgid "Followers"
@@ -1011,7 +1015,7 @@ msgstr "ホーム"
 #: src/view/screens/PreferencesHomeFeed.tsx:95
 #: src/view/screens/Settings.tsx:481
 msgid "Home Feed Preferences"
-msgstr "ホームフィード設定"
+msgstr "ホームフィードの設定"
 
 #: src/view/com/auth/login/ForgotPasswordForm.tsx:114
 msgid "Hosting provider"
@@ -1144,7 +1148,7 @@ msgstr "このフィードをいいね"
 #: src/view/screens/PostLikedBy.tsx:27
 #: src/view/screens/ProfileFeedLikedBy.tsx:27
 msgid "Liked by"
-msgstr "いいねした人"
+msgstr "いいねしたユーザー"
 
 #: src/view/screens/Profile.tsx:164
 msgid "Likes"
@@ -1156,7 +1160,7 @@ msgstr "いいね"
 
 #: src/view/com/modals/CreateOrEditList.tsx:186
 msgid "List Avatar"
-msgstr "リストアバター"
+msgstr "リストのアバター"
 
 #: src/view/com/modals/CreateOrEditList.tsx:199
 msgid "List Name"
@@ -1172,15 +1176,15 @@ msgstr "リスト"
 #: src/view/com/post-thread/PostThread.tsx:259
 #: src/view/com/post-thread/PostThread.tsx:267
 msgid "Load more posts"
-msgstr "より多くの投稿をロード"
+msgstr "投稿をさらにロード"
 
 #: src/view/screens/Notifications.tsx:144
 msgid "Load new notifications"
-msgstr "新しい通知をロード"
+msgstr "最新の通知をロード"
 
 #: src/view/com/feeds/FeedPage.tsx:189
 msgid "Load new posts"
-msgstr "投稿をロード"
+msgstr "最新の投稿をロード"
 
 #: src/view/com/composer/text-input/mobile/Autocomplete.tsx:95
 msgid "Loading..."
@@ -1278,19 +1282,19 @@ msgstr "スレッドをミュート"
 
 #: src/view/screens/Moderation.tsx:109
 msgid "Muted accounts"
-msgstr "ミュートされたアカウント"
+msgstr "ミュート中のアカウント"
 
 #: src/view/screens/ModerationMutedAccounts.tsx:107
 msgid "Muted Accounts"
-msgstr "ミュートされたアカウント"
+msgstr "ミュート中のアカウント"
 
 #: src/view/screens/ModerationMutedAccounts.tsx:115
 msgid "Muted accounts have their posts removed from your feed and from your notifications. Mutes are completely private."
-msgstr "ミュートをされたアカウントは、フィードと通知からの投稿が削除されます。ミュートの設定は完全に非公開です。"
+msgstr "ミュート中のアカウントの投稿は、フィードや通知から取り除かれます。ミュートの設定は完全に非公開です。"
 
 #: src/view/screens/ProfileList.tsx:272
 msgid "Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them."
-msgstr "ミュートは非公開です。ミュートをされたアカウントはあなたと引き続き関わることができますが、そのアカウントの投稿や通知を受信することはできません。"
+msgstr "ミュートの設定は非公開です。ミュート中のアカウントはあなたと引き続き関わることができますが、そのアカウントの投稿や通知を受信することはできません。"
 
 #: src/view/com/modals/BirthDateSettings.tsx:56
 msgid "My Birthday"
@@ -1410,7 +1414,7 @@ msgstr "ちょっと!"
 msgid "Okay"
 msgstr "OK"
 
-#: src/view/com/composer/Composer.tsx:354
+#: src/view/com/composer/Composer.tsx:358
 msgid "One or more images is missing alt text."
 msgstr "1つもしくは複数の画像にALTテキストがありません。"
 
@@ -1538,7 +1542,11 @@ msgstr ""
 #~ msgid "Please tell us why you think this decision was incorrect."
 #~ msgstr "この判断が誤っていると考える理由を教えてください。"
 
-#: src/view/com/composer/Composer.tsx:337
+#: src/view/com/composer/Composer.tsx:214
+msgid "Please wait for your link card to finish loading"
+msgstr ""
+
+#: src/view/com/composer/Composer.tsx:341
 #: src/view/com/post-thread/PostThread.tsx:225
 #: src/view/screens/PostThread.tsx:80
 msgid "Post"
@@ -1726,7 +1734,7 @@ msgstr "リポストまたは引用"
 
 #: src/view/screens/PostRepostedBy.tsx:27
 msgid "Reposted by"
-msgstr "リポストした人"
+msgstr "リポストしたユーザー"
 
 #: src/view/com/modals/ChangeEmail.tsx:181
 #: src/view/com/modals/ChangeEmail.tsx:183
@@ -1881,15 +1889,15 @@ msgstr "新しいパスワードを設定"
 
 #: src/view/screens/PreferencesHomeFeed.tsx:216
 msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible."
-msgstr "フィードから全ての引用を非表示にするには、この設定を「いいえ」にします。リポストは引き続き表示されます。"
+msgstr "フィード内の引用をすべて非表示にするには、この設定を「いいえ」にします。リポストは引き続き表示されます。"
 
 #: src/view/screens/PreferencesHomeFeed.tsx:113
 msgid "Set this setting to \"No\" to hide all replies from your feed."
-msgstr "フィードから全ての返信を非表示にするには、この設定を「いいえ」にします。"
+msgstr "フィード内の返信をすべて非表示にするには、この設定を「いいえ」にします。"
 
 #: src/view/screens/PreferencesHomeFeed.tsx:182
 msgid "Set this setting to \"No\" to hide all reposts from your feed."
-msgstr "フィードから全てのリポストを非表示にするには、この設定を「いいえ」にします。"
+msgstr "フィード内のリポストをすべて非表示にするには、この設定を「いいえ」にします。"
 
 #: src/view/screens/PreferencesThreads.tsx:116
 msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature."
@@ -1943,7 +1951,7 @@ msgstr "返信を表示"
 
 #: src/view/screens/PreferencesThreads.tsx:94
 msgid "Show replies by people you follow before all other replies."
-msgstr "他のすべての返信の前に、フォローしている人からの返信を表示します。"
+msgstr "自分がフォローしているユーザーからの返信を、他のすべての返信の前に表示します。"
 
 #: src/view/screens/PreferencesHomeFeed.tsx:179
 msgid "Show Reposts"
@@ -2364,13 +2372,13 @@ msgstr "アルゴリズムによるフィードにはどの言語を使用しま
 #: src/view/com/composer/threadgate/ThreadgateBtn.tsx:47
 #: src/view/com/modals/Threadgate.tsx:66
 msgid "Who can reply"
-msgstr "返信できる人"
+msgstr "返信できるユーザー"
 
 #: src/view/com/modals/crop-image/CropImage.web.tsx:102
 msgid "Wide"
 msgstr "ワイド"
 
-#: src/view/com/composer/Composer.tsx:409
+#: src/view/com/composer/Composer.tsx:413
 msgid "Write post"
 msgstr "投稿を書く"
 
diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx
index a189f20e4..abaabbf0e 100644
--- a/src/state/queries/notifications/unread.tsx
+++ b/src/state/queries/notifications/unread.tsx
@@ -89,6 +89,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
         // update & broadcast
         setNumUnread('')
         broadcast.postMessage({event: ''})
+        if (isNative) {
+          Notifications.setBadgeCountAsync(0)
+        }
       },
 
       async checkUnread({invalidate}: {invalidate?: boolean} = {}) {
diff --git a/src/state/queries/notifications/util.ts b/src/state/queries/notifications/util.ts
index 438879b7e..411a0f791 100644
--- a/src/state/queries/notifications/util.ts
+++ b/src/state/queries/notifications/util.ts
@@ -156,7 +156,7 @@ async function fetchSubjects(
 ): Promise<Map<string, AppBskyFeedDefs.PostView>> {
   const uris = new Set<string>()
   for (const notif of groupedNotifs) {
-    if (notif.subjectUri) {
+    if (notif.subjectUri && !notif.subjectUri.includes('feed.generator')) {
       uris.add(notif.subjectUri)
     }
   }
@@ -216,6 +216,8 @@ function getSubjectUri(
         ? notif.record.subject?.uri
         : undefined
     }
+  } else if (type === 'feedgen-like') {
+    return notif.reasonSubject
   }
 }
 
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 64427b837..9f60923d6 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -207,7 +207,11 @@ export const ComposePost = observer(function ComposePost({
     setError('')
 
     if (richtext.text.trim().length === 0 && gallery.isEmpty && !extLink) {
-      setError('Did you want to say anything?')
+      setError(_(msg`Did you want to say anything?`))
+      return
+    }
+    if (extLink?.isLoading) {
+      setError(_(msg`Please wait for your link card to finish loading`))
       return
     }
 
@@ -438,7 +442,7 @@ export const ComposePost = observer(function ComposePost({
                   accessibilityLabel={_(msg`Add link card`)}
                   accessibilityHint={`Creates a card with a thumbnail. The card links to ${url}`}>
                   <Text style={pal.text}>
-                    <Trans>Add link card:</Trans>
+                    <Trans>Add link card:</Trans>{' '}
                     <Text style={[pal.link, s.ml5]}>{toShortUrl(url)}</Text>
                   </Text>
                 </TouchableOpacity>
diff --git a/src/view/com/modals/Threadgate.tsx b/src/view/com/modals/Threadgate.tsx
index 9d78a2e6d..0deef185b 100644
--- a/src/view/com/modals/Threadgate.tsx
+++ b/src/view/com/modals/Threadgate.tsx
@@ -69,7 +69,7 @@ export function Component({
 
       <ScrollView>
         <Text style={[pal.text, styles.description]}>
-          Choose "Everybody" or "Nobody"
+          <Trans>Choose "Everybody" or "Nobody"</Trans>
         </Text>
         <View style={{flexDirection: 'row', gap: 6, paddingHorizontal: 6}}>
           <Selectable
@@ -86,7 +86,7 @@ export function Component({
           />
         </View>
         <Text style={[pal.text, styles.description]}>
-          Or combine these options:
+          <Trans>Or combine these options:</Trans>
         </Text>
         <View style={{flexDirection: 'column', gap: 4, paddingHorizontal: 6}}>
           <Selectable
diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx
index aaa2ea2c6..24b7e4fb6 100644
--- a/src/view/com/notifications/FeedItem.tsx
+++ b/src/view/com/notifications/FeedItem.tsx
@@ -42,6 +42,7 @@ import {TimeElapsed} from '../util/TimeElapsed'
 import {isWeb} from 'platform/detection'
 import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import {FeedSourceCard} from '../feeds/FeedSourceCard'
 
 const MAX_AUTHORS = 5
 
@@ -112,7 +113,7 @@ let FeedItem = ({
     ]
   }, [item, moderationOpts])
 
-  if (item.subjectUri && !item.subject) {
+  if (item.subjectUri && !item.subject && item.type !== 'feedgen-like') {
     // don't render anything if the target post was deleted or unfindable
     return <View />
   }
@@ -166,7 +167,7 @@ let FeedItem = ({
     iconStyle = [s.blue3 as FontAwesomeIconStyle]
   } else if (item.type === 'feedgen-like') {
     action = `liked your custom feed${
-      item.subjectUri ? ` '${new AtUri(item.subjectUri).rkey}}'` : ''
+      item.subjectUri ? ` '${new AtUri(item.subjectUri).rkey}'` : ''
     }`
     icon = 'HeartIconSolid'
     iconStyle = [
@@ -256,6 +257,13 @@ let FeedItem = ({
         {item.type === 'post-like' || item.type === 'repost' ? (
           <AdditionalPostText post={item.subject} />
         ) : null}
+        {item.type === 'feedgen-like' && item.subjectUri ? (
+          <FeedSourceCard
+            feedUri={item.subjectUri}
+            style={[pal.view, pal.border, styles.feedcard]}
+            showLikes
+          />
+        ) : null}
       </View>
     </Link>
   )
@@ -496,6 +504,12 @@ const styles = StyleSheet.create({
     marginLeft: 2,
     opacity: 0.8,
   },
+  feedcard: {
+    borderWidth: 1,
+    borderRadius: 8,
+    paddingVertical: 12,
+    marginTop: 6,
+  },
 
   addedContainer: {
     paddingTop: 4,
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 6a377cdf6..986fd70b2 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -186,9 +186,9 @@ let PostThreadItemLoaded = ({
     return makeProfileLink(post.author, 'post', urip.rkey, 'reposted-by')
   }, [post.uri, post.author])
   const repostsTitle = 'Reposts of this post'
-  const isSelfLabeledPost =
+  const isModeratedPost =
     moderation.decisions.post.cause?.type === 'label' &&
-    moderation.decisions.post.cause.label.src === currentAccount?.did
+    moderation.decisions.post.cause.label.src !== currentAccount?.did
 
   const translatorUrl = getTranslatorLink(
     record?.text || '',
@@ -335,7 +335,7 @@ let PostThreadItemLoaded = ({
               postUri={post.uri}
               record={record}
               showAppealLabelItem={
-                post.author.did === currentAccount?.did && !isSelfLabeledPost
+                post.author.did === currentAccount?.did && isModeratedPost
               }
               style={{
                 paddingVertical: 6,
@@ -539,6 +539,7 @@ let PostThreadItemLoaded = ({
                   timestamp={post.indexedAt}
                   postHref={postHref}
                   showAvatar={isThreadedChild}
+                  avatarModeration={moderation.avatar}
                   avatarSize={28}
                   displayNameType="md-bold"
                   displayNameStyle={isThreadedChild && s.ml2}
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index 2be314266..942d7bf71 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -34,6 +34,7 @@ import {countLines} from 'lib/strings/helpers'
 import {useComposerControls} from '#/state/shell/composer'
 import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
 import {FeedNameText} from '../util/FeedInfoText'
+import {useSession} from '#/state/session'
 
 export function FeedItem({
   post,
@@ -102,10 +103,14 @@ let FeedItemInner = ({
 }): React.ReactNode => {
   const {openComposer} = useComposerControls()
   const pal = usePalette('default')
+  const {currentAccount} = useSession()
   const href = useMemo(() => {
     const urip = new AtUri(post.uri)
     return makeProfileLink(post.author, 'post', urip.rkey)
   }, [post.uri, post.author])
+  const isModeratedPost =
+    moderation.decisions.post.cause?.type === 'label' &&
+    moderation.decisions.post.cause.label.src !== currentAccount?.did
 
   const replyAuthorDid = useMemo(() => {
     if (!record?.reply) {
@@ -284,7 +289,14 @@ let FeedItemInner = ({
             postEmbed={post.embed}
             postAuthor={post.author}
           />
-          <PostCtrls post={post} record={record} onPressReply={onPressReply} />
+          <PostCtrls
+            post={post}
+            record={record}
+            onPressReply={onPressReply}
+            showAppealLabelItem={
+              post.author.did === currentAccount?.did && isModeratedPost
+            }
+          />
         </View>
       </View>
     </Link>
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index eef7094cd..b9c3842b3 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -11,6 +11,7 @@ import {sanitizeHandle} from 'lib/strings/handles'
 import {isAndroid} from 'platform/detection'
 import {TimeElapsed} from './TimeElapsed'
 import {makeProfileLink} from 'lib/routes/links'
+import {ModerationUI} from '@atproto/api'
 
 interface PostMetaOpts {
   author: {
@@ -23,6 +24,7 @@ interface PostMetaOpts {
   postHref: string
   timestamp: string
   showAvatar?: boolean
+  avatarModeration?: ModerationUI
   avatarSize?: number
   displayNameType?: TypographyVariant
   displayNameStyle?: StyleProp<TextStyle>
@@ -41,7 +43,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
           <UserAvatar
             avatar={opts.author.avatar}
             size={opts.avatarSize || 16}
-            // TODO moderation
+            moderation={opts.avatarModeration}
           />
         </View>
       )}
diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx
index 414fb1e09..a50b52175 100644
--- a/src/view/com/util/post-ctrls/PostCtrls.tsx
+++ b/src/view/com/util/post-ctrls/PostCtrls.tsx
@@ -31,12 +31,14 @@ let PostCtrls = ({
   big,
   post,
   record,
+  showAppealLabelItem,
   style,
   onPressReply,
 }: {
   big?: boolean
   post: Shadow<AppBskyFeedDefs.PostView>
   record: AppBskyFeedPost.Record
+  showAppealLabelItem?: boolean
   style?: StyleProp<ViewStyle>
   onPressReply: () => void
 }): React.ReactNode => {
@@ -207,6 +209,7 @@ let PostCtrls = ({
           postCid={post.cid}
           postUri={post.uri}
           record={record}
+          showAppealLabelItem={showAppealLabelItem}
           style={styles.ctrlPad}
         />
       )}
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index e28a67e37..6e2f18305 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -1,6 +1,6 @@
 import React from 'react'
 import {View} from 'react-native'
-import {useFocusEffect} from '@react-navigation/native'
+import {useFocusEffect, useIsFocused} from '@react-navigation/native'
 import {useQueryClient} from '@tanstack/react-query'
 import {
   NativeStackScreenProps,
@@ -46,6 +46,7 @@ export function NotificationsScreen({}: Props) {
   const unreadNotifs = useUnreadNotifications()
   const unreadApi = useUnreadNotificationsApi()
   const hasNew = !!unreadNotifs
+  const isScreenFocused = useIsFocused()
 
   // event handlers
   // =
@@ -83,8 +84,11 @@ export function NotificationsScreen({}: Props) {
     }, [screen, setMinimalShellMode]),
   )
   React.useEffect(() => {
+    if (!isScreenFocused) {
+      return
+    }
     return listenSoftReset(onPressLoadLatest)
-  }, [onPressLoadLatest])
+  }, [onPressLoadLatest, isScreenFocused])
 
   const ListHeaderComponent = React.useCallback(() => {
     if (isDesktop) {
diff --git a/src/view/screens/PreferencesHomeFeed.tsx b/src/view/screens/PreferencesHomeFeed.tsx
index fe17be5e8..20ef72923 100644
--- a/src/view/screens/PreferencesHomeFeed.tsx
+++ b/src/view/screens/PreferencesHomeFeed.tsx
@@ -117,7 +117,7 @@ export function PreferencesHomeFeed({navigation}: Props) {
             <ToggleButton
               testID="toggleRepliesBtn"
               type="default-light"
-              label={showReplies ? 'Yes' : 'No'}
+              label={showReplies ? _(msg`Yes`) : _(msg`No`)}
               isSelected={showReplies}
               onPress={() =>
                 setFeedViewPref({
diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx
index cde39a33f..211306c0d 100644
--- a/src/view/screens/ProfileFeed.tsx
+++ b/src/view/screens/ProfileFeed.tsx
@@ -57,6 +57,7 @@ import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like'
 import {useComposerControls} from '#/state/shell/composer'
 import {truncateAndInvalidate} from '#/state/queries/util'
 import {isNative} from '#/platform/detection'
+import {listenSoftReset} from '#/state/events'
 
 const SECTION_TITLES = ['Posts', 'About']
 
@@ -446,6 +447,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
     const [hasNew, setHasNew] = React.useState(false)
     const [isScrolledDown, setIsScrolledDown] = React.useState(false)
     const queryClient = useQueryClient()
+    const isScreenFocused = useIsFocused()
 
     const onScrollToTop = useCallback(() => {
       scrollElRef.current?.scrollToOffset({
@@ -460,6 +462,13 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
       scrollToTop: onScrollToTop,
     }))
 
+    React.useEffect(() => {
+      if (!isScreenFocused) {
+        return
+      }
+      return listenSoftReset(onScrollToTop)
+    }, [onScrollToTop, isScreenFocused])
+
     const renderPostsEmpty = useCallback(() => {
       return <EmptyState icon="feed" message="This feed is empty!" />
     }, [])
diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx
index 2db768cc5..c51758ae5 100644
--- a/src/view/screens/ProfileList.tsx
+++ b/src/view/screens/ProfileList.tsx
@@ -57,6 +57,7 @@ import {
 } from '#/state/queries/preferences'
 import {logger} from '#/logger'
 import {useAnalytics} from '#/lib/analytics/analytics'
+import {listenSoftReset} from '#/state/events'
 
 const SECTION_TITLES_CURATE = ['Posts', 'About']
 const SECTION_TITLES_MOD = ['About']
@@ -601,6 +602,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
     const queryClient = useQueryClient()
     const [hasNew, setHasNew] = React.useState(false)
     const [isScrolledDown, setIsScrolledDown] = React.useState(false)
+    const isScreenFocused = useIsFocused()
 
     const onScrollToTop = useCallback(() => {
       scrollElRef.current?.scrollToOffset({
@@ -614,6 +616,13 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
       scrollToTop: onScrollToTop,
     }))
 
+    React.useEffect(() => {
+      if (!isScreenFocused) {
+        return
+      }
+      return listenSoftReset(onScrollToTop)
+    }, [onScrollToTop, isScreenFocused])
+
     const renderPostsEmpty = useCallback(() => {
       return <EmptyState icon="feed" message="This feed is empty!" />
     }, [])
diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx
index 60fd21674..b522edfba 100644
--- a/src/view/screens/Search/Search.tsx
+++ b/src/view/screens/Search/Search.tsx
@@ -212,12 +212,17 @@ function SearchScreenPostResults({query}: {query: string}) {
   const items = React.useMemo(() => {
     let temp: SearchResultSlice[] = []
 
+    const seenUris = new Set()
     for (const post of posts) {
+      if (seenUris.has(post.uri)) {
+        continue
+      }
       temp.push({
         type: 'post',
         key: post.uri,
         post,
       })
+      seenUris.add(post.uri)
     }
 
     if (isFetchingNextPage) {