From 433fa33b27c46d978388e0034d832c133a37d0b9 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 6 May 2025 23:07:21 +0300 Subject: [PATCH 1/9] bump `hls.js` (#8336) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2fab82975a..7213da03d1 100644 --- a/package.json +++ b/package.json @@ -160,7 +160,7 @@ "expo-web-browser": "~14.1.6", "fast-text-encoding": "^1.0.6", "history": "^5.3.0", - "hls.js": "^1.5.11", + "hls.js": "^1.6.2", "js-sha256": "^0.9.0", "jwt-decode": "^4.0.0", "lande": "^1.0.10", diff --git a/yarn.lock b/yarn.lock index 20458e9065..34fb2f6980 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12351,10 +12351,10 @@ history@^5.3.0: dependencies: "@babel/runtime" "^7.7.6" -hls.js@^1.5.11: - version "1.5.11" - resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.5.11.tgz#3941347df454983859ae8c75fe19e8818719a826" - integrity sha512-q3We1izi2+qkOO+TvZdHv+dx6aFzdtk3xc1/Qesrvto4thLTT/x/1FK85c5h1qZE4MmMBNgKg+MIW8nxQfxwBw== +hls.js@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.6.2.tgz#02272bea644b5f61f71741256618d6b629ae7834" + integrity sha512-rx+pETSCJEDThm/JCm8CuadcAC410cVjb1XVXFNDKFuylaayHk1+tFxhkjvnMDAfqsJHxZXDAJ3Uc2d5xQyWlQ== hmac-drbg@^1.0.1: version "1.0.1" From d1ec73a28968a5fa290c37f49512c20117da12d8 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 6 May 2025 23:07:48 +0300 Subject: [PATCH 2/9] hide footer when lists list is empty (#8337) --- src/view/com/feeds/ProfileFeedgens.tsx | 10 +++++++++- src/view/com/lists/ProfileLists.tsx | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/view/com/feeds/ProfileFeedgens.tsx b/src/view/com/feeds/ProfileFeedgens.tsx index 76b57d0eed..5ba17e4260 100644 --- a/src/view/com/feeds/ProfileFeedgens.tsx +++ b/src/view/com/feeds/ProfileFeedgens.tsx @@ -182,6 +182,7 @@ export const ProfileFeedgens = React.forwardRef< }, [enabled, scrollElRef, setScrollViewTag]) const ProfileFeedgensFooter = React.useCallback(() => { + if (isEmpty) return null return ( ) - }, [hasNextPage, error, isFetchingNextPage, headerOffset, fetchNextPage]) + }, [ + hasNextPage, + error, + isFetchingNextPage, + headerOffset, + fetchNextPage, + isEmpty, + ]) return ( diff --git a/src/view/com/lists/ProfileLists.tsx b/src/view/com/lists/ProfileLists.tsx index e264bd6c64..8fea510812 100644 --- a/src/view/com/lists/ProfileLists.tsx +++ b/src/view/com/lists/ProfileLists.tsx @@ -178,6 +178,7 @@ export const ProfileLists = React.forwardRef( }, [enabled, scrollElRef, setScrollViewTag]) const ProfileListsFooter = React.useCallback(() => { + if (isEmpty) return null return ( ( height={180 + headerOffset} /> ) - }, [hasNextPage, error, isFetchingNextPage, headerOffset, fetchNextPage]) + }, [ + hasNextPage, + error, + isFetchingNextPage, + headerOffset, + fetchNextPage, + isEmpty, + ]) return ( From e01b24702e6e4ef2e5c77de726f4a10f418eef2e Mon Sep 17 00:00:00 2001 From: pfrazee <1270099+pfrazee@users.noreply.github.com> Date: Wed, 7 May 2025 02:39:23 +0000 Subject: [PATCH 3/9] Nightly source-language update --- src/locale/locales/en/messages.po | 549 ++++++++++++++++-------------- 1 file changed, 291 insertions(+), 258 deletions(-) diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index 122e9afe4f..e5e0cd20b6 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -151,7 +151,7 @@ msgstr "" msgid "{0} reacted {1} to {2}" msgstr "" -#: src/view/com/util/UserAvatar.tsx:464 +#: src/view/com/util/UserAvatar.tsx:514 msgid "{0}'s avatar" msgstr "" @@ -486,7 +486,7 @@ msgstr "" #: src/screens/Messages/components/RequestButtons.tsx:81 #: src/view/com/profile/ProfileMenu.tsx:147 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:392 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:404 msgctxt "toast" msgid "Account blocked" msgstr "" @@ -497,7 +497,7 @@ msgid "Account followed" msgstr "" #: src/view/com/profile/ProfileMenu.tsx:123 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:415 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:427 msgctxt "toast" msgid "Account muted" msgstr "" @@ -519,7 +519,7 @@ msgstr "" msgid "Account removed from quick access" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:140 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:127 #: src/view/com/profile/ProfileMenu.tsx:137 msgctxt "toast" msgid "Account unblocked" @@ -531,7 +531,7 @@ msgid "Account unfollowed" msgstr "" #: src/view/com/profile/ProfileMenu.tsx:113 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:405 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:417 msgctxt "toast" msgid "Account unmuted" msgstr "" @@ -579,7 +579,7 @@ msgstr "" msgid "Add alt text" msgstr "" -#: src/view/com/composer/videos/SubtitleDialog.tsx:107 +#: src/view/com/composer/videos/SubtitleDialog.tsx:106 msgid "Add alt text (optional)" msgstr "" @@ -774,10 +774,10 @@ msgstr "" #: src/screens/Settings/AccessibilitySettings.tsx:56 #: src/view/com/composer/GifAltText.tsx:154 #: src/view/com/composer/photos/ImageAltTextDialog.tsx:118 -#: src/view/com/composer/videos/SubtitleDialog.tsx:41 -#: src/view/com/composer/videos/SubtitleDialog.tsx:56 -#: src/view/com/composer/videos/SubtitleDialog.tsx:102 -#: src/view/com/composer/videos/SubtitleDialog.tsx:106 +#: src/view/com/composer/videos/SubtitleDialog.tsx:40 +#: src/view/com/composer/videos/SubtitleDialog.tsx:55 +#: src/view/com/composer/videos/SubtitleDialog.tsx:101 +#: src/view/com/composer/videos/SubtitleDialog.tsx:105 msgid "Alt text" msgstr "" @@ -925,7 +925,7 @@ msgstr "" msgid "App Icon" msgstr "" -#: src/screens/Settings/LanguageSettings.tsx:80 +#: src/screens/Settings/LanguageSettings.tsx:87 msgid "App Language" msgstr "" @@ -1134,9 +1134,9 @@ msgstr "" msgid "Birthday" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:309 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:296 #: src/view/com/profile/ProfileMenu.tsx:435 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:849 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:881 msgid "Block" msgstr "" @@ -1146,13 +1146,13 @@ msgstr "" #: src/screens/Messages/components/RequestButtons.tsx:136 #: src/view/com/profile/ProfileMenu.tsx:346 #: src/view/com/profile/ProfileMenu.tsx:353 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:686 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:688 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:718 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:720 msgid "Block account" msgstr "" #: src/view/com/profile/ProfileMenu.tsx:418 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:844 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:876 msgid "Block Account?" msgstr "" @@ -1203,7 +1203,7 @@ msgid "Blocked Accounts" msgstr "" #: src/view/com/profile/ProfileMenu.tsx:430 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:846 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:878 msgid "Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you." msgstr "" @@ -1227,7 +1227,7 @@ msgstr "" msgid "Blocking will not prevent labels from being applied on your account, but it will stop this account from replying in your threads or interacting with you." msgstr "" -#: src/view/com/auth/SplashScreen.web.tsx:173 +#: src/view/com/auth/SplashScreen.web.tsx:175 msgid "Blog" msgstr "" @@ -1340,7 +1340,7 @@ msgstr "" msgid "Browse topic {displayName}" msgstr "" -#: src/view/com/auth/SplashScreen.web.tsx:168 +#: src/view/com/auth/SplashScreen.web.tsx:170 msgid "Business" msgstr "" @@ -1371,7 +1371,7 @@ msgstr "" msgid "By you" msgstr "" -#: src/view/com/composer/photos/OpenCameraBtn.tsx:72 +#: src/view/com/composer/photos/OpenCameraBtn.tsx:70 msgid "Camera" msgstr "" @@ -1381,8 +1381,8 @@ msgstr "" #: src/components/Prompt.tsx:138 #: src/components/Prompt.tsx:140 #: src/screens/Deactivated.tsx:158 -#: src/screens/Profile/Header/EditProfileDialog.tsx:227 -#: src/screens/Profile/Header/EditProfileDialog.tsx:235 +#: src/screens/Profile/Header/EditProfileDialog.tsx:225 +#: src/screens/Profile/Header/EditProfileDialog.tsx:233 #: src/screens/Search/Shell.tsx:349 #: src/screens/Settings/AppIconSettings/index.tsx:44 #: src/screens/Settings/AppIconSettings/index.tsx:225 @@ -1393,9 +1393,11 @@ msgstr "" #: src/screens/Takendown.tsx:102 #: src/view/com/composer/Composer.tsx:937 #: src/view/com/composer/Composer.tsx:948 +#: src/view/com/composer/photos/EditImageDialog.web.tsx:43 +#: src/view/com/composer/photos/EditImageDialog.web.tsx:52 #: src/view/com/modals/ChangePassword.tsx:279 #: src/view/com/modals/ChangePassword.tsx:282 -#: src/view/com/modals/CreateOrEditList.tsx:335 +#: src/view/com/modals/CreateOrEditList.tsx:333 #: src/view/com/modals/CropImage.web.tsx:97 #: src/view/com/modals/EditProfile.tsx:269 #: src/view/com/modals/LinkWarning.tsx:105 @@ -1405,7 +1407,7 @@ msgstr "" msgid "Cancel" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:340 +#: src/view/com/modals/CreateOrEditList.tsx:338 #: src/view/com/modals/DeleteAccount.tsx:170 #: src/view/com/modals/DeleteAccount.tsx:278 msgctxt "action" @@ -1442,18 +1444,18 @@ msgid "Cancels opening the linked website" msgstr "" #: src/state/shell/composer/index.tsx:82 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:121 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:162 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:198 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:123 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:164 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:200 msgid "Cannot interact with a blocked user" msgstr "" -#: src/view/com/composer/videos/SubtitleDialog.tsx:133 +#: src/view/com/composer/videos/SubtitleDialog.tsx:132 msgid "Captions (.vtt)" msgstr "" -#: src/view/com/composer/videos/SubtitleDialog.tsx:41 -#: src/view/com/composer/videos/SubtitleDialog.tsx:56 +#: src/view/com/composer/videos/SubtitleDialog.tsx:40 +#: src/view/com/composer/videos/SubtitleDialog.tsx:55 msgid "Captions & alt text" msgstr "" @@ -1470,6 +1472,10 @@ msgstr "" msgid "Change app icon to \"{0}\"" msgstr "" +#: src/components/AppLanguageDropdown.tsx:40 +msgid "Change app language" +msgstr "" + #: src/screens/Settings/AccountSettings.tsx:99 #: src/screens/Settings/AccountSettings.tsx:103 msgid "Change email" @@ -1865,8 +1871,8 @@ msgstr "" msgid "Connecting..." msgstr "" -#: src/screens/Signup/index.tsx:193 -#: src/screens/Signup/index.tsx:196 +#: src/screens/Signup/index.tsx:197 +#: src/screens/Signup/index.tsx:200 msgid "Contact support" msgstr "" @@ -1896,7 +1902,7 @@ msgstr "" msgid "Content from across the network we think you might like." msgstr "" -#: src/screens/Settings/LanguageSettings.tsx:250 +#: src/screens/Settings/LanguageSettings.tsx:153 #: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:76 msgid "Content Languages" msgstr "" @@ -1922,7 +1928,7 @@ msgid "Context menu backdrop, click to close the menu." msgstr "" #: src/screens/Onboarding/StepInterests/index.tsx:244 -#: src/screens/Onboarding/StepProfile/index.tsx:278 +#: src/screens/Onboarding/StepProfile/index.tsx:276 msgid "Continue" msgstr "" @@ -1935,7 +1941,7 @@ msgid "Continue thread..." msgstr "" #: src/screens/Onboarding/StepInterests/index.tsx:241 -#: src/screens/Onboarding/StepProfile/index.tsx:275 +#: src/screens/Onboarding/StepProfile/index.tsx:273 #: src/screens/Signup/BackNextButtons.tsx:60 msgid "Continue to next step" msgstr "" @@ -1966,8 +1972,8 @@ msgstr "" #: src/lib/sharing.ts:25 #: src/lib/sharing.ts:41 #: src/view/com/modals/InviteCodes.tsx:153 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:250 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:397 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:255 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:400 msgid "Copied to clipboard" msgstr "" @@ -1993,8 +1999,8 @@ msgstr "" msgid "Copy at:// URI" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:748 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:750 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:780 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:782 msgid "Copy author DID" msgstr "" @@ -2025,8 +2031,8 @@ msgstr "" msgid "Copy link to list" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:498 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:507 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:517 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:526 msgid "Copy link to post" msgstr "" @@ -2035,13 +2041,13 @@ msgstr "" msgid "Copy message text" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:741 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:743 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:773 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:775 msgid "Copy post at:// URI" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:476 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:478 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:495 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:497 msgid "Copy post text" msgstr "" @@ -2122,7 +2128,7 @@ msgstr "" msgid "Create an account without using this starter pack" msgstr "" -#: src/screens/Onboarding/StepProfile/index.tsx:292 +#: src/screens/Onboarding/StepProfile/index.tsx:290 msgid "Create an avatar instead" msgstr "" @@ -2222,7 +2228,7 @@ msgstr "" #: src/screens/StarterPack/StarterPackScreen.tsx:594 #: src/screens/StarterPack/StarterPackScreen.tsx:672 #: src/screens/StarterPack/StarterPackScreen.tsx:744 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:767 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:799 #: src/view/screens/ProfileList.tsx:768 msgid "Delete" msgstr "" @@ -2285,8 +2291,8 @@ msgid "Delete my account" msgstr "" #: src/view/com/composer/Composer.tsx:856 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:726 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:728 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:758 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:760 msgid "Delete post" msgstr "" @@ -2303,7 +2309,7 @@ msgstr "" msgid "Delete this list?" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:762 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:794 msgid "Delete this post?" msgstr "" @@ -2320,19 +2326,19 @@ msgstr "" msgid "Deleted post." msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:370 -#: src/view/com/modals/CreateOrEditList.tsx:280 -#: src/view/com/modals/CreateOrEditList.tsx:301 +#: src/screens/Profile/Header/EditProfileDialog.tsx:369 +#: src/view/com/modals/CreateOrEditList.tsx:278 +#: src/view/com/modals/CreateOrEditList.tsx:299 #: src/view/com/modals/EditProfile.tsx:218 #: src/view/com/modals/EditProfile.tsx:230 msgid "Description" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:390 +#: src/screens/Profile/Header/EditProfileDialog.tsx:389 msgid "Description is too long" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:391 +#: src/screens/Profile/Header/EditProfileDialog.tsx:390 msgid "Description is too long. {DESCRIPTION_MAX_GRAPHEMES, plural, other {The maximum number of characters is #.}}" msgstr "" @@ -2341,12 +2347,12 @@ msgstr "" msgid "Descriptive alt text" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:630 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:640 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:662 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:672 msgid "Detach quote" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:824 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:856 msgid "Detach quote post?" msgstr "" @@ -2456,9 +2462,9 @@ msgstr "" msgid "Display larger alt text badges" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:321 -#: src/screens/Profile/Header/EditProfileDialog.tsx:327 -#: src/screens/Profile/Header/EditProfileDialog.tsx:377 +#: src/screens/Profile/Header/EditProfileDialog.tsx:320 +#: src/screens/Profile/Header/EditProfileDialog.tsx:326 +#: src/screens/Profile/Header/EditProfileDialog.tsx:376 #: src/view/com/modals/EditProfile.tsx:194 msgid "Display name" msgstr "" @@ -2467,11 +2473,11 @@ msgstr "" msgid "Display Name" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:340 +#: src/screens/Profile/Header/EditProfileDialog.tsx:339 msgid "Display name is too long" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:341 +#: src/screens/Profile/Header/EditProfileDialog.tsx:340 msgid "Display name is too long. {DISPLAY_NAME_MAX_GRAPHEMES, plural, other {The maximum number of characters is #.}}" msgstr "" @@ -2501,16 +2507,18 @@ msgstr "" #: src/components/dms/ReportDialog.tsx:314 #: src/components/forms/DateField/index.tsx:102 #: src/components/forms/DateField/index.tsx:108 -#: src/screens/Onboarding/StepProfile/index.tsx:330 -#: src/screens/Onboarding/StepProfile/index.tsx:333 +#: src/components/Select/index.tsx:183 +#: src/components/Select/index.tsx:190 +#: src/screens/Onboarding/StepProfile/index.tsx:328 +#: src/screens/Onboarding/StepProfile/index.tsx:331 #: src/screens/Settings/components/AddAppPasswordDialog.tsx:215 #: src/screens/Settings/components/AddAppPasswordDialog.tsx:222 #: src/view/com/auth/server-input/index.tsx:232 #: src/view/com/auth/server-input/index.tsx:233 #: src/view/com/composer/labels/LabelsBtn.tsx:223 #: src/view/com/composer/labels/LabelsBtn.tsx:230 -#: src/view/com/composer/videos/SubtitleDialog.tsx:169 -#: src/view/com/composer/videos/SubtitleDialog.tsx:179 +#: src/view/com/composer/videos/SubtitleDialog.tsx:168 +#: src/view/com/composer/videos/SubtitleDialog.tsx:178 #: src/view/com/modals/CropImage.web.tsx:112 #: src/view/com/modals/InviteCodes.tsx:81 #: src/view/com/modals/InviteCodes.tsx:124 @@ -2560,7 +2568,7 @@ msgstr "" msgid "e.g. alice" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:328 +#: src/screens/Profile/Header/EditProfileDialog.tsx:327 msgid "e.g. Alice Lastname" msgstr "" @@ -2580,19 +2588,19 @@ msgstr "" msgid "E.g. artistic nudes." msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:263 +#: src/view/com/modals/CreateOrEditList.tsx:261 msgid "e.g. Great Posters" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:264 +#: src/view/com/modals/CreateOrEditList.tsx:262 msgid "e.g. Spammers" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:292 +#: src/view/com/modals/CreateOrEditList.tsx:290 msgid "e.g. The posters who never miss." msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/CreateOrEditList.tsx:291 msgid "e.g. Users that repeatedly reply with ads." msgstr "" @@ -2613,8 +2621,8 @@ msgctxt "action" msgid "Edit" msgstr "" -#: src/view/com/util/UserAvatar.tsx:363 -#: src/view/com/util/UserBanner.tsx:95 +#: src/view/com/util/UserAvatar.tsx:398 +#: src/view/com/util/UserBanner.tsx:119 msgid "Edit avatar" msgstr "" @@ -2622,14 +2630,14 @@ msgstr "" msgid "Edit Feeds" msgstr "" -#: src/view/com/composer/photos/EditImageDialog.web.tsx:58 -#: src/view/com/composer/photos/EditImageDialog.web.tsx:62 +#: src/view/com/composer/photos/EditImageDialog.web.tsx:85 +#: src/view/com/composer/photos/EditImageDialog.web.tsx:89 #: src/view/com/composer/photos/Gallery.tsx:194 msgid "Edit image" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:707 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:720 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:739 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:752 msgid "Edit interaction settings" msgstr "" @@ -2642,7 +2650,7 @@ msgstr "" msgid "Edit list details" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:230 +#: src/view/com/modals/CreateOrEditList.tsx:228 msgid "Edit Moderation List" msgstr "" @@ -2664,15 +2672,15 @@ msgstr "" msgid "Edit post interaction settings" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:276 -#: src/screens/Profile/Header/EditProfileDialog.tsx:282 -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:196 -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:182 +#: src/screens/Profile/Header/EditProfileDialog.tsx:275 +#: src/screens/Profile/Header/EditProfileDialog.tsx:281 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:183 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:169 msgid "Edit profile" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:199 -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:185 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:186 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:172 msgid "Edit Profile" msgstr "" @@ -2680,7 +2688,7 @@ msgstr "" msgid "Edit starter pack" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:225 +#: src/view/com/modals/CreateOrEditList.tsx:223 msgid "Edit User List" msgstr "" @@ -2745,8 +2753,8 @@ msgstr "" #: src/components/dialogs/Embed.tsx:104 #: src/components/dialogs/Embed.tsx:108 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:515 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:517 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:534 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:536 msgid "Embed post" msgstr "" @@ -2820,7 +2828,7 @@ msgstr "" msgid "End of feed" msgstr "" -#: src/view/com/composer/videos/SubtitleDialog.tsx:159 +#: src/view/com/composer/videos/SubtitleDialog.tsx:158 msgid "Ensure you have selected a language for each subtitle file." msgstr "" @@ -3068,7 +3076,7 @@ msgstr "" msgid "Failed to create starter pack" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:186 +#: src/view/com/modals/CreateOrEditList.tsx:184 msgid "Failed to create the list. Check your internet connection and try again." msgstr "" @@ -3082,7 +3090,7 @@ msgstr "" msgid "Failed to delete message" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:210 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:215 msgid "Failed to delete post, please try again" msgstr "" @@ -3161,7 +3169,7 @@ msgstr "" msgid "Failed to submit appeal, please try again." msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:239 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:244 msgid "Failed to toggle thread mute, please try again" msgstr "" @@ -3207,8 +3215,8 @@ msgstr "" msgid "Feedback" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:294 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:303 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:299 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:314 msgctxt "toast" msgid "Feedback sent!" msgstr "" @@ -3309,7 +3317,7 @@ msgstr "" #: src/components/ProfileCard.tsx:412 #: src/components/ProfileHoverCard/index.web.tsx:456 #: src/components/ProfileHoverCard/index.web.tsx:467 -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:236 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:223 #: src/screens/VideoFeed/index.tsx:831 #: src/view/com/post-thread/PostThreadFollowBtn.tsx:131 msgid "Follow" @@ -3320,7 +3328,7 @@ msgctxt "action" msgid "Follow" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:221 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:208 #: src/view/com/post-thread/PostThreadFollowBtn.tsx:113 msgid "Follow {0}" msgstr "" @@ -3352,7 +3360,7 @@ msgstr "" msgid "Follow all" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:234 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:221 #: src/view/com/post-thread/PostThreadFollowBtn.tsx:129 msgid "Follow Back" msgstr "" @@ -3391,7 +3399,7 @@ msgstr "" #: src/components/ProfileCard.tsx:406 #: src/components/ProfileHoverCard/index.web.tsx:455 #: src/components/ProfileHoverCard/index.web.tsx:466 -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:232 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:219 #: src/screens/VideoFeed/index.tsx:829 #: src/view/com/post-thread/PostThreadFollowBtn.tsx:134 #: src/view/screens/Feeds.tsx:599 @@ -3400,7 +3408,7 @@ msgid "Following" msgstr "" #: src/components/ProfileCard.tsx:369 -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:101 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:88 msgid "Following {0}" msgstr "" @@ -3485,7 +3493,7 @@ msgstr "" msgid "From @{sanitizedAuthor}" msgstr "" -#: src/view/com/posts/PostFeedItem.tsx:284 +#: src/view/com/posts/PostFeedItem.tsx:294 msgctxt "from-feed" msgid "From <0/>" msgstr "" @@ -3515,7 +3523,7 @@ msgstr "" msgid "GIF" msgstr "" -#: src/screens/Onboarding/StepProfile/index.tsx:234 +#: src/screens/Onboarding/StepProfile/index.tsx:232 msgid "Give your profile a face" msgstr "" @@ -3634,7 +3642,7 @@ msgstr "" msgid "Hashtag {tag}" msgstr "" -#: src/screens/Signup/index.tsx:191 +#: src/screens/Signup/index.tsx:195 msgid "Having trouble?" msgstr "" @@ -3646,7 +3654,7 @@ msgstr "" msgid "Help" msgstr "" -#: src/screens/Onboarding/StepProfile/index.tsx:237 +#: src/screens/Onboarding/StepProfile/index.tsx:235 msgid "Help people know you're not a bot by uploading a picture or creating an avatar." msgstr "" @@ -3676,7 +3684,7 @@ msgstr "" #: src/lib/moderation/useLabelBehaviorDescription.ts:20 #: src/lib/moderation/useLabelBehaviorDescription.ts:25 #: src/lib/moderation/useLabelBehaviorDescription.ts:30 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:778 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:810 #: src/view/shell/desktop/SidebarTrendingTopics.tsx:110 msgid "Hide" msgstr "" @@ -3690,18 +3698,18 @@ msgstr "" msgid "Hide customization options" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:587 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:593 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:619 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:625 msgid "Hide post for me" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:604 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:614 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:636 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:646 msgid "Hide reply for everyone" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:586 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:592 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:618 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:624 msgid "Hide reply for me" msgstr "" @@ -3709,12 +3717,12 @@ msgstr "" msgid "Hide this card" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:773 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:805 msgid "Hide this post?" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:773 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:834 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:805 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:866 msgid "Hide this reply?" msgstr "" @@ -3846,7 +3854,7 @@ msgstr "" msgid "If you have your own domain, you can use that as your handle. This lets you self-verify your identity. <0>Learn more here." msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:764 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:796 msgid "If you remove this post, you won't be able to recover it." msgstr "" @@ -4002,7 +4010,7 @@ msgid "Job ID: {0}" msgstr "" #. Link to a page with job openings at Bluesky -#: src/view/com/auth/SplashScreen.web.tsx:178 +#: src/view/com/auth/SplashScreen.web.tsx:180 msgid "Jobs" msgstr "" @@ -4060,7 +4068,7 @@ msgstr "" msgid "Language Settings" msgstr "" -#: src/screens/Settings/LanguageSettings.tsx:71 +#: src/screens/Settings/LanguageSettings.tsx:78 #: src/screens/Settings/Settings.tsx:208 #: src/screens/Settings/Settings.tsx:211 msgid "Languages" @@ -4083,7 +4091,7 @@ msgstr "" #: src/components/verification/VerificationsDialog.tsx:161 #: src/components/verification/VerifierDialog.tsx:128 #: src/screens/Moderation/VerificationSettings.tsx:47 -#: src/screens/Profile/Header/EditProfileDialog.tsx:360 +#: src/screens/Profile/Header/EditProfileDialog.tsx:359 #: src/screens/Settings/components/ChangeHandleDialog.tsx:212 #: src/view/com/modals/EditProfile.tsx:207 msgid "Learn more" @@ -4093,7 +4101,7 @@ msgstr "" msgid "Learn More" msgstr "" -#: src/view/com/auth/SplashScreen.web.tsx:166 +#: src/view/com/auth/SplashScreen.web.tsx:168 msgid "Learn more about Bluesky" msgstr "" @@ -4186,7 +4194,7 @@ msgstr "" msgid "Like" msgstr "" -#: src/view/com/util/post-ctrls/PostCtrls.tsx:315 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:317 msgid "Like ({0, plural, one {# like} other {# likes}})" msgstr "" @@ -4203,7 +4211,7 @@ msgstr "" msgid "Like this feed" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:285 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:272 msgid "Like this labeler" msgstr "" @@ -4225,8 +4233,8 @@ msgstr "" #: src/components/LabelingServiceCard/index.tsx:96 #: src/screens/Profile/components/ProfileFeedHeader.tsx:485 -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:305 -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:319 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:292 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:306 msgid "Liked by {likeCount, plural, one {# user} other {# users}}" msgstr "" @@ -4247,7 +4255,7 @@ msgstr "" msgid "List" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:241 +#: src/view/com/modals/CreateOrEditList.tsx:239 msgid "List Avatar" msgstr "" @@ -4287,7 +4295,7 @@ msgctxt "toast" msgid "List muted" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:255 +#: src/view/com/modals/CreateOrEditList.tsx:253 msgid "List Name" msgstr "" @@ -4518,12 +4526,12 @@ msgstr "" msgid "Moderation list by you" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:177 +#: src/view/com/modals/CreateOrEditList.tsx:175 msgctxt "toast" msgid "Moderation list created" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:163 +#: src/view/com/modals/CreateOrEditList.tsx:161 msgctxt "toast" msgid "Moderation list updated" msgstr "" @@ -4600,8 +4608,8 @@ msgstr "" #: src/view/com/profile/ProfileMenu.tsx:325 #: src/view/com/profile/ProfileMenu.tsx:332 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:669 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:675 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:701 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:707 msgid "Mute account" msgstr "" @@ -4650,13 +4658,13 @@ msgstr "" msgid "Mute this word until you unmute it" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:553 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:557 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:585 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:589 msgid "Mute thread" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:567 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:569 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:599 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:601 msgid "Mute words & tags" msgstr "" @@ -4694,11 +4702,11 @@ msgstr "" msgid "My Feeds" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:270 +#: src/view/com/modals/CreateOrEditList.tsx:268 msgid "Name" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:135 +#: src/view/com/modals/CreateOrEditList.tsx:133 msgid "Name is required" msgstr "" @@ -4793,7 +4801,7 @@ msgstr "" msgid "New messages" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:232 +#: src/view/com/modals/CreateOrEditList.tsx:230 msgid "New Moderation List" msgstr "" @@ -4828,7 +4836,7 @@ msgstr "" msgid "New user info dialog" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:227 +#: src/view/com/modals/CreateOrEditList.tsx:225 msgid "New User List" msgstr "" @@ -4890,7 +4898,7 @@ msgid "No likes yet" msgstr "" #: src/components/ProfileCard.tsx:391 -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:122 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:109 msgid "No longer following {0}" msgstr "" @@ -5003,8 +5011,8 @@ msgid "Not Found" msgstr "" #: src/view/com/profile/ProfileMenu.tsx:442 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:791 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:361 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:823 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:363 msgid "Note about sharing" msgstr "" @@ -5077,7 +5085,7 @@ msgid "Oh no! Something went wrong." msgstr "" #. Confirm button text. -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:362 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:349 #: src/screens/Search/modules/ExploreInterestsCard.tsx:48 #: src/screens/Settings/AppIconSettings/index.tsx:48 #: src/screens/Settings/AppIconSettings/index.tsx:229 @@ -5128,7 +5136,7 @@ msgstr "" msgid "Only contains letters, numbers, and hyphens" msgstr "" -#: src/lib/media/picker.shared.ts:29 +#: src/lib/media/picker.shared.ts:32 msgid "Only image files are supported" msgstr "" @@ -5158,7 +5166,7 @@ msgstr "" msgid "Open {name} profile shortcut menu" msgstr "" -#: src/screens/Onboarding/StepProfile/index.tsx:286 +#: src/screens/Onboarding/StepProfile/index.tsx:284 msgid "Open avatar creator" msgstr "" @@ -5209,7 +5217,7 @@ msgstr "" msgid "Open pack" msgstr "" -#: src/view/com/util/forms/PostDropdownBtn.tsx:70 +#: src/view/com/util/forms/PostDropdownBtn.tsx:73 msgid "Open post options menu" msgstr "" @@ -5242,15 +5250,15 @@ msgstr "" msgid "Opens additional details for a debug entry" msgstr "" -#: src/view/com/composer/videos/SubtitleDialog.tsx:45 +#: src/view/com/composer/videos/SubtitleDialog.tsx:44 msgid "Opens alt text dialog" msgstr "" -#: src/view/com/composer/photos/OpenCameraBtn.tsx:73 +#: src/view/com/composer/photos/OpenCameraBtn.tsx:71 msgid "Opens camera on device" msgstr "" -#: src/view/com/composer/videos/SubtitleDialog.tsx:44 +#: src/view/com/composer/videos/SubtitleDialog.tsx:43 msgid "Opens captions and alt text dialog" msgstr "" @@ -5301,7 +5309,7 @@ msgid "Opens the linked website" msgstr "" #: src/view/com/notifications/NotificationFeedItem.tsx:790 -#: src/view/com/util/UserAvatar.tsx:465 +#: src/view/com/util/UserAvatar.tsx:515 msgid "Opens this profile" msgstr "" @@ -5468,12 +5476,12 @@ msgstr "" msgid "Pin to Home" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:444 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:451 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:463 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:470 msgid "Pin to your profile" msgstr "" -#: src/view/com/posts/PostFeedItem.tsx:365 +#: src/view/com/posts/PostFeedItem.tsx:375 msgid "Pinned" msgstr "" @@ -5644,7 +5652,7 @@ msgstr "" msgid "Post by @{0}" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:190 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:195 msgctxt "toast" msgid "Post deleted" msgstr "" @@ -5745,7 +5753,7 @@ msgstr "" msgid "Previous image" msgstr "" -#: src/screens/Settings/LanguageSettings.tsx:166 +#: src/screens/Settings/LanguageSettings.tsx:120 msgid "Primary Language" msgstr "" @@ -5803,7 +5811,7 @@ msgstr "" msgid "Profile" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:198 +#: src/screens/Profile/Header/EditProfileDialog.tsx:196 #: src/view/com/modals/EditProfile.tsx:128 msgctxt "toast" msgid "Profile updated" @@ -5860,11 +5868,11 @@ msgstr "" msgid "Quote post" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:331 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:343 msgid "Quote post was re-attached" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:330 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:342 msgid "Quote post was successfully detached" msgstr "" @@ -5898,8 +5906,8 @@ msgstr "" msgid "Rate limit exceeded – you've tried to change your handle too many times in a short period. Please wait a minute before trying again." msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:629 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:639 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:661 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:671 msgid "Re-attach quote" msgstr "" @@ -5924,7 +5932,7 @@ msgstr "" msgid "Read more" msgstr "" -#: src/view/com/auth/SplashScreen.web.tsx:171 +#: src/view/com/auth/SplashScreen.web.tsx:173 msgid "Read the Bluesky blog" msgstr "" @@ -6002,13 +6010,13 @@ msgstr "" msgid "Remove attachment" msgstr "" -#: src/view/com/util/UserAvatar.tsx:416 -#: src/view/com/util/UserAvatar.tsx:419 +#: src/view/com/util/UserAvatar.tsx:457 +#: src/view/com/util/UserAvatar.tsx:460 msgid "Remove Avatar" msgstr "" -#: src/view/com/util/UserBanner.tsx:152 -#: src/view/com/util/UserBanner.tsx:155 +#: src/view/com/util/UserBanner.tsx:186 +#: src/view/com/util/UserBanner.tsx:189 msgid "Remove Banner" msgstr "" @@ -6070,7 +6078,7 @@ msgstr "" msgid "Remove repost" msgstr "" -#: src/view/com/composer/videos/SubtitleDialog.tsx:260 +#: src/view/com/composer/videos/SubtitleDialog.tsx:261 msgid "Remove subtitle file" msgstr "" @@ -6150,7 +6158,7 @@ msgctxt "action" msgid "Reply" msgstr "" -#: src/view/com/util/post-ctrls/PostCtrls.tsx:267 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:269 msgid "Reply ({0, plural, one {# reply} other {# replies}})" msgstr "" @@ -6177,33 +6185,33 @@ msgid "Reply sorting" msgstr "" #: src/view/com/post/Post.tsx:204 -#: src/view/com/posts/PostFeedItem.tsx:559 +#: src/view/com/posts/PostFeedItem.tsx:570 msgctxt "description" msgid "Reply to <0><1/>" msgstr "" -#: src/view/com/posts/PostFeedItem.tsx:550 +#: src/view/com/posts/PostFeedItem.tsx:561 msgctxt "description" msgid "Reply to a blocked post" msgstr "" -#: src/view/com/posts/PostFeedItem.tsx:552 +#: src/view/com/posts/PostFeedItem.tsx:563 msgctxt "description" msgid "Reply to a post" msgstr "" #: src/view/com/post/Post.tsx:202 -#: src/view/com/posts/PostFeedItem.tsx:556 +#: src/view/com/posts/PostFeedItem.tsx:567 msgctxt "description" msgid "Reply to you" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:363 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:375 msgctxt "toast" msgid "Reply visibility updated" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:362 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:374 msgid "Reply was successfully hidden" msgstr "" @@ -6245,8 +6253,8 @@ msgstr "" msgid "Report message" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:695 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:697 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:727 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:729 msgid "Report post" msgstr "" @@ -6295,6 +6303,11 @@ msgstr "" msgid "Report this user" msgstr "" +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:566 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:569 +msgid "Report topic misclassification" +msgstr "" + #: src/view/com/util/post-ctrls/RepostButton.tsx:156 #: src/view/com/util/post-ctrls/RepostButton.tsx:167 msgctxt "action" @@ -6321,16 +6334,16 @@ msgstr "" msgid "Reposted By" msgstr "" -#: src/view/com/posts/PostFeedItem.tsx:305 +#: src/view/com/posts/PostFeedItem.tsx:315 msgid "Reposted by {0}" msgstr "" -#: src/view/com/posts/PostFeedItem.tsx:324 +#: src/view/com/posts/PostFeedItem.tsx:334 msgid "Reposted by <0><1/>" msgstr "" -#: src/view/com/posts/PostFeedItem.tsx:303 -#: src/view/com/posts/PostFeedItem.tsx:322 +#: src/view/com/posts/PostFeedItem.tsx:313 +#: src/view/com/posts/PostFeedItem.tsx:332 msgid "Reposted by you" msgstr "" @@ -6458,23 +6471,23 @@ msgstr "" #: src/components/dialogs/PostInteractionSettingsDialog.tsx:479 #: src/components/dialogs/PostInteractionSettingsDialog.tsx:485 #: src/components/StarterPack/QrCodeDialog.tsx:191 -#: src/screens/Profile/Header/EditProfileDialog.tsx:245 -#: src/screens/Profile/Header/EditProfileDialog.tsx:259 +#: src/screens/Profile/Header/EditProfileDialog.tsx:243 +#: src/screens/Profile/Header/EditProfileDialog.tsx:257 #: src/screens/Settings/components/ChangeHandleDialog.tsx:262 #: src/view/com/composer/GifAltText.tsx:193 #: src/view/com/composer/GifAltText.tsx:202 -#: src/view/com/composer/photos/EditImageDialog.web.tsx:77 -#: src/view/com/composer/photos/EditImageDialog.web.tsx:83 +#: src/view/com/composer/photos/EditImageDialog.web.tsx:62 +#: src/view/com/composer/photos/EditImageDialog.web.tsx:75 #: src/view/com/composer/photos/ImageAltTextDialog.tsx:153 #: src/view/com/composer/photos/ImageAltTextDialog.tsx:163 -#: src/view/com/modals/CreateOrEditList.tsx:317 +#: src/view/com/modals/CreateOrEditList.tsx:315 #: src/view/com/modals/EditProfile.tsx:244 #: src/view/screens/SavedFeeds.tsx:109 msgid "Save" msgstr "" #: src/view/com/lightbox/ImageViewing/index.tsx:610 -#: src/view/com/modals/CreateOrEditList.tsx:325 +#: src/view/com/modals/CreateOrEditList.tsx:323 msgctxt "action" msgid "Save" msgstr "" @@ -6653,7 +6666,7 @@ msgstr "" msgid "See #{tag} posts by user" msgstr "" -#: src/view/com/auth/SplashScreen.web.tsx:176 +#: src/view/com/auth/SplashScreen.web.tsx:178 msgid "See jobs at Bluesky" msgstr "" @@ -6673,6 +6686,10 @@ msgstr "" msgid "Select account" msgstr "" +#: src/components/AppLanguageDropdown.tsx:60 +msgid "Select an app language" +msgstr "" + #: src/screens/Onboarding/StepProfile/AvatarCircle.tsx:66 msgid "Select an avatar" msgstr "" @@ -6681,7 +6698,15 @@ msgstr "" msgid "Select an emoji" msgstr "" -#: src/screens/Settings/LanguageSettings.tsx:261 +#: src/components/Select/index.tsx:204 +msgid "Select an option" +msgstr "" + +#: src/screens/Settings/LanguageSettings.tsx:98 +msgid "Select app language" +msgstr "" + +#: src/screens/Settings/LanguageSettings.tsx:164 msgid "Select content languages" msgstr "" @@ -6701,11 +6726,11 @@ msgstr "" msgid "Select how long to mute this word for." msgstr "" -#: src/view/com/composer/videos/SubtitleDialog.tsx:245 +#: src/view/com/composer/videos/SubtitleDialog.tsx:248 msgid "Select language..." msgstr "" -#: src/screens/Settings/LanguageSettings.tsx:275 +#: src/screens/Settings/LanguageSettings.tsx:178 msgid "Select languages" msgstr "" @@ -6717,6 +6742,10 @@ msgstr "" msgid "Select moderator" msgstr "" +#: src/screens/Settings/LanguageSettings.tsx:131 +msgid "Select primary language" +msgstr "" + #: src/view/com/composer/videos/SubtitleFilePicker.tsx:59 #: src/view/com/composer/videos/SubtitleFilePicker.tsx:66 msgid "Select subtitle file (.vtt)" @@ -6738,11 +6767,11 @@ msgstr "" msgid "Select what content this mute word should apply to." msgstr "" -#: src/screens/Settings/LanguageSettings.tsx:84 +#: src/screens/Settings/LanguageSettings.tsx:91 msgid "Select which language to use for the app's user interface." msgstr "" -#: src/screens/Settings/LanguageSettings.tsx:254 +#: src/screens/Settings/LanguageSettings.tsx:157 msgid "Select which languages you want your subscribed feeds to include. If none are selected, all languages will be shown." msgstr "" @@ -6755,7 +6784,7 @@ msgstr "" msgid "Select your interests from the options below" msgstr "" -#: src/screens/Settings/LanguageSettings.tsx:170 +#: src/screens/Settings/LanguageSettings.tsx:124 msgid "Select your preferred language for translations in your feed." msgstr "" @@ -6817,8 +6846,8 @@ msgstr "" msgid "Send verification email" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:487 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:490 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:506 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:509 msgid "Send via direct message" msgstr "" @@ -6877,9 +6906,9 @@ msgstr "" #: src/screens/Topic.tsx:102 #: src/view/com/profile/ProfileMenu.tsx:229 #: src/view/com/profile/ProfileMenu.tsx:238 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:498 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:507 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:350 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:517 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:526 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:352 #: src/view/screens/ProfileList.tsx:513 msgid "Share" msgstr "" @@ -6898,8 +6927,8 @@ msgid "Share a fun fact!" msgstr "" #: src/view/com/profile/ProfileMenu.tsx:447 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:796 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:366 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:828 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:368 msgid "Share anyway" msgstr "" @@ -6982,8 +7011,8 @@ msgstr "" msgid "Show hidden replies" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:537 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:539 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:556 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:558 msgid "Show less like this" msgstr "" @@ -6993,12 +7022,12 @@ msgstr "" #: src/view/com/post-thread/PostThreadItem.tsx:625 #: src/view/com/post/Post.tsx:243 -#: src/view/com/posts/PostFeedItem.tsx:515 +#: src/view/com/posts/PostFeedItem.tsx:526 msgid "Show More" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:529 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:531 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:548 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:550 msgid "Show more like this" msgstr "" @@ -7028,8 +7057,8 @@ msgstr "" msgid "Show replies by people you follow before all other replies" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:603 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:613 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:635 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:645 msgid "Show reply for everyone" msgstr "" @@ -7354,11 +7383,11 @@ msgstr "" msgid "Subscribe to @{0} to use these labels:" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:248 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:235 msgid "Subscribe to Labeler" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:214 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:201 msgid "Subscribe to this labeler" msgstr "" @@ -7471,7 +7500,7 @@ msgstr "" msgid "Tell a joke!" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:378 +#: src/screens/Profile/Header/EditProfileDialog.tsx:377 msgid "Tell us a bit about yourself" msgstr "" @@ -7517,6 +7546,10 @@ msgstr "" msgid "Text input field" msgstr "" +#: src/view/com/posts/ShowLessFollowup.tsx:39 +msgid "Thank you for your feedback! It has been sent to the feed operator." +msgstr "" + #: src/components/dialogs/VerifyEmailDialog.tsx:125 msgid "Thank you! Your email has been successfully verified." msgstr "" @@ -7554,7 +7587,7 @@ msgstr "" msgid "That's everything!" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:305 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:292 #: src/view/com/profile/ProfileMenu.tsx:423 msgid "The account will be able to interact with you after unblocking." msgstr "" @@ -7670,7 +7703,7 @@ msgid "There was an issue contacting the server" msgstr "" #: src/screens/Profile/components/ProfileFeedHeader.tsx:411 -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:112 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:111 msgid "There was an issue contacting the server, please check your internet connection and try again." msgstr "" @@ -7684,7 +7717,7 @@ msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" #: src/screens/Search/Explore.tsx:908 -#: src/view/com/posts/PostFeed.tsx:607 +#: src/view/com/posts/PostFeed.tsx:653 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -7720,9 +7753,9 @@ msgstr "" msgid "There was an issue updating your feeds, please check your internet connection and try again." msgstr "" -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:110 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:97 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:118 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:131 -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:144 #: src/view/com/post-thread/PostThreadFollowBtn.tsx:90 #: src/view/com/post-thread/PostThreadFollowBtn.tsx:101 #: src/view/com/profile/ProfileMenu.tsx:117 @@ -7731,9 +7764,9 @@ msgstr "" #: src/view/com/profile/ProfileMenu.tsx:151 #: src/view/com/profile/ProfileMenu.tsx:164 #: src/view/com/profile/ProfileMenu.tsx:176 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:396 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:409 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:419 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:408 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:421 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:431 msgid "There was an issue! {0}" msgstr "" @@ -7900,12 +7933,12 @@ msgstr "" msgid "This post has been deleted." msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:793 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:363 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:825 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:365 msgid "This post is only visible to logged-in users. It won't be visible to people who aren't signed in." msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:775 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:807 msgid "This post will be hidden from feeds and threads. This cannot be undone." msgstr "" @@ -7917,7 +7950,7 @@ msgstr "" msgid "This profile is only visible to logged-in users. It won't be visible to people who aren't signed in." msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:836 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:868 msgid "This reply will be sorted into a hidden section at the bottom of your thread and will mute notifications for subsequent replies - both for yourself and others." msgstr "" @@ -7974,7 +8007,7 @@ msgstr "" msgid "This will remove @{0} from the quick access list." msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:826 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:858 msgid "This will remove your post from this quote post for all users, and replace it with a placeholder." msgstr "" @@ -8055,8 +8088,8 @@ msgstr "" #: src/components/dms/MessageContextMenu.tsx:141 #: src/view/com/post-thread/PostThreadItem.tsx:818 #: src/view/com/post-thread/PostThreadItem.tsx:821 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:468 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:470 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:487 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:489 msgid "Translate" msgstr "" @@ -8123,14 +8156,14 @@ msgstr "" #: src/components/dms/MessagesListBlockedFooter.tsx:104 #: src/components/dms/MessagesListBlockedFooter.tsx:112 #: src/components/dms/MessagesListBlockedFooter.tsx:119 -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:200 -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:309 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:187 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:296 #: src/view/com/profile/ProfileMenu.tsx:435 #: src/view/screens/ProfileList.tsx:723 msgid "Unblock" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:205 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:192 msgctxt "action" msgid "Unblock" msgstr "" @@ -8142,7 +8175,7 @@ msgstr "" msgid "Unblock account" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:303 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:290 #: src/view/com/profile/ProfileMenu.tsx:417 msgid "Unblock Account?" msgstr "" @@ -8165,7 +8198,7 @@ msgctxt "action" msgid "Unfollow" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:220 +#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:207 msgid "Unfollow {0}" msgstr "" @@ -8190,7 +8223,7 @@ msgstr "" msgid "Unlike" msgstr "" -#: src/view/com/util/post-ctrls/PostCtrls.tsx:309 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:311 msgid "Unlike ({0, plural, one {# like} other {# likes}})" msgstr "" @@ -8211,8 +8244,8 @@ msgstr "" #: src/view/com/profile/ProfileMenu.tsx:324 #: src/view/com/profile/ProfileMenu.tsx:330 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:668 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:674 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:700 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:706 msgid "Unmute account" msgstr "" @@ -8224,8 +8257,8 @@ msgstr "" msgid "Unmute list" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:553 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:557 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:585 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:589 msgid "Unmute thread" msgstr "" @@ -8251,8 +8284,8 @@ msgstr "" msgid "Unpin from home" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:443 -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:450 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:462 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:469 msgid "Unpin from profile" msgstr "" @@ -8273,7 +8306,7 @@ msgstr "" msgid "Unsnooze email reminder" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:246 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:233 msgid "Unsubscribe" msgstr "" @@ -8282,7 +8315,7 @@ msgstr "" msgid "Unsubscribe from list" msgstr "" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:213 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:200 msgid "Unsubscribe from this labeler" msgstr "" @@ -8319,12 +8352,12 @@ msgstr "" msgid "Update to {domain}" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:335 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:347 msgctxt "toast" msgid "Updating quote attachment failed" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:367 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:379 msgctxt "toast" msgid "Updating reply visibility failed" msgstr "" @@ -8333,7 +8366,7 @@ msgstr "" msgid "Updating..." msgstr "" -#: src/screens/Onboarding/StepProfile/index.tsx:290 +#: src/screens/Onboarding/StepProfile/index.tsx:288 msgid "Upload a photo instead" msgstr "" @@ -8341,22 +8374,22 @@ msgstr "" msgid "Upload a text file to:" msgstr "" -#: src/view/com/util/UserAvatar.tsx:387 -#: src/view/com/util/UserAvatar.tsx:390 -#: src/view/com/util/UserBanner.tsx:123 -#: src/view/com/util/UserBanner.tsx:126 +#: src/view/com/util/UserAvatar.tsx:428 +#: src/view/com/util/UserAvatar.tsx:431 +#: src/view/com/util/UserBanner.tsx:157 +#: src/view/com/util/UserBanner.tsx:160 msgid "Upload from Camera" msgstr "" -#: src/view/com/util/UserAvatar.tsx:404 -#: src/view/com/util/UserBanner.tsx:140 +#: src/view/com/util/UserAvatar.tsx:445 +#: src/view/com/util/UserBanner.tsx:174 msgid "Upload from Files" msgstr "" -#: src/view/com/util/UserAvatar.tsx:398 -#: src/view/com/util/UserAvatar.tsx:402 -#: src/view/com/util/UserBanner.tsx:134 -#: src/view/com/util/UserBanner.tsx:138 +#: src/view/com/util/UserAvatar.tsx:439 +#: src/view/com/util/UserAvatar.tsx:443 +#: src/view/com/util/UserBanner.tsx:168 +#: src/view/com/util/UserBanner.tsx:172 msgid "Upload from Library" msgstr "" @@ -8446,12 +8479,12 @@ msgstr "" msgid "User list by you" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:176 +#: src/view/com/modals/CreateOrEditList.tsx:174 msgctxt "toast" msgid "User list created" msgstr "" -#: src/view/com/modals/CreateOrEditList.tsx:162 +#: src/view/com/modals/CreateOrEditList.tsx:160 msgctxt "toast" msgid "User list updated" msgstr "" @@ -8595,7 +8628,7 @@ msgstr "" msgid "Video not found." msgstr "" -#: src/view/com/composer/videos/SubtitleDialog.tsx:99 +#: src/view/com/composer/videos/SubtitleDialog.tsx:98 msgid "Video settings" msgstr "" @@ -8847,7 +8880,7 @@ msgstr "" msgid "We're sorry! We can't find the page you were looking for." msgstr "" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:356 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:343 msgid "We're sorry! You can only subscribe to twenty labelers, and you've reached your limit of twenty." msgstr "" @@ -8982,11 +9015,11 @@ msgstr "" msgid "Yes, delete this starter pack" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:829 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:861 msgid "Yes, detach" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:839 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:871 msgid "Yes, hide" msgstr "" @@ -9026,7 +9059,7 @@ msgstr "" msgid "You are verified" msgstr "" -#: src/screens/Profile/Header/EditProfileDialog.tsx:356 +#: src/screens/Profile/Header/EditProfileDialog.tsx:355 #: src/view/com/modals/EditProfile.tsx:203 msgid "You are verified. You will lose your verification status if you change your display name. <0>Learn more." msgstr "" @@ -9191,7 +9224,7 @@ msgstr "" msgid "You may only add up to 3 feeds" msgstr "" -#: src/lib/media/picker.shared.ts:22 +#: src/lib/media/picker.shared.ts:25 msgid "You may only select up to 4 images" msgstr "" @@ -9240,11 +9273,11 @@ msgstr "" msgid "You will be signed out of all your accounts." msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:232 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:237 msgid "You will no longer receive notifications for this thread" msgstr "" -#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:228 +#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:233 msgid "You will now receive notifications for this thread" msgstr "" From 0edd3bd3b4445275ea3f9ddfc5f91ad4950acdd8 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 7 May 2025 17:06:49 +0300 Subject: [PATCH 4/9] fix select triggers on ios safari (#8343) --- src/components/Select/index.web.tsx | 1 + src/components/Select/types.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Select/index.web.tsx b/src/components/Select/index.web.tsx index e9d26631c5..0e6fa85ca3 100644 --- a/src/components/Select/index.web.tsx +++ b/src/components/Select/index.web.tsx @@ -73,6 +73,7 @@ export function Trigger({children, label}: TriggerProps) { }, props: { ...props, + onPress: props.onClick, onFocus: onFocus, onBlur: onBlur, onMouseEnter, diff --git a/src/components/Select/types.ts b/src/components/Select/types.ts index 5c1b80a3be..661621a603 100644 --- a/src/components/Select/types.ts +++ b/src/components/Select/types.ts @@ -53,7 +53,9 @@ export type RadixPassThroughTriggerProps = { ['aria-controls']?: string ['aria-haspopup']?: boolean ['aria-expanded']?: AccessibilityProps['aria-expanded'] - onPress: () => void + onClick: () => void + onPointerDown: () => void + onKeyDown: () => void } export type TriggerProps = { From 0f96669f8c0d578d888c06496d97929130d34a1f Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Wed, 7 May 2025 10:23:33 -0500 Subject: [PATCH 5/9] [APP-1158] Refactor email-related dialogs (#8296) * WIP * Update email * Fire off confirmation email after change * Verify step, integrate stateful control * Remove tentative EnterCode step * Handle token step * Handle instructions and integrate into 2FA setting * Fix load state when reusing same email * Add new state * Add 2FA screens * Clean up state in Update step * Clean up verify state, handle normal callback * Normalize convetions * Add verification reminder screen * Improve session refresh * Handle verification requirements for composer and convo * Fix lint * Do better * Couple missing translations * Format * Use listeners for easier to grok logic * Clean errors * Move to global context * [APP-1158] Gate features by email verification state (#8305) * Use new hook in all locations * Format * Seems to work, not great duplication * Wrap all open composer calls * Remove unneeded spans * Missed one * Fix handler on Conversation * Gate new chat in header * Add comment * Remove whoopsie * Format * add hackfix for dialog not showing * add prompt to accept chat btn * navigation not necessary * send back one screen, rather than home * Update comment --------- Co-authored-by: Samuel Newman * Clear dialog state Co-authored-by: Samuel Newman * Update icon * Check color * Add 2FA warning * Update instructions * Fix X button * Use an effect silly goose * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/dialaUpdate copyogs/EmailDialog/screens/Update.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy * Update copy * Update copy * Update copy * Update copy Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update copy * Add link back to update email from verify email dialog * Handle token field validation --------- Co-authored-by: Samuel Newman Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> --- src/Navigation.tsx | 13 +- src/components/Dialog/index.web.tsx | 8 +- .../StarterPack/ProfileStarterPacks.tsx | 50 +-- src/components/dialogs/Context.tsx | 15 +- .../components/ResendEmailText.tsx | 56 +++ .../EmailDialog/components/TokenField.tsx | 45 ++ .../EmailDialog/data/useAccountEmailState.ts | 79 ++++ .../EmailDialog/data/useConfirmEmail.ts | 29 ++ .../EmailDialog/data/useManageEmail2FA.ts | 35 ++ .../EmailDialog/data/useRequestEmailUpdate.ts | 13 + .../data/useRequestEmailVerification.ts | 13 + .../EmailDialog/data/useUpdateEmail.ts | 45 ++ src/components/dialogs/EmailDialog/events.ts | 23 ++ src/components/dialogs/EmailDialog/index.tsx | 71 ++++ .../EmailDialog/screens/Manage2FA/Disable.tsx | 254 ++++++++++++ .../EmailDialog/screens/Manage2FA/Enable.tsx | 137 +++++++ .../EmailDialog/screens/Manage2FA/index.tsx | 70 ++++ .../dialogs/EmailDialog/screens/Update.tsx | 319 +++++++++++++++ .../screens/VerificationReminder.tsx | 99 +++++ .../dialogs/EmailDialog/screens/Verify.tsx | 386 ++++++++++++++++++ src/components/dialogs/EmailDialog/types.ts | 38 ++ src/components/dms/MessageProfileButton.tsx | 43 +- src/components/dms/dialogs/NewChatDialog.tsx | 35 +- src/lib/hooks/useCleanError.ts | 91 +++++ src/lib/hooks/useIntentHandler.ts | 4 +- src/lib/hooks/useOpenComposer.tsx | 22 + src/lib/hooks/useRequireEmailVerification.tsx | 53 +++ src/screens/Messages/ChatList.tsx | 15 +- src/screens/Messages/Conversation.tsx | 61 ++- .../Messages/components/RequestButtons.tsx | 34 +- .../Messages/components/RequestListItem.tsx | 2 +- src/screens/Profile/ProfileFeed/index.tsx | 21 +- src/screens/Settings/AccountSettings.tsx | 33 +- .../Settings/components/Email2FAToggle.tsx | 65 +-- src/screens/VideoFeed/index.tsx | 40 +- src/state/shell/composer/index.tsx | 14 +- .../composer/useComposerKeyboardShortcut.tsx | 4 +- src/state/shell/index.tsx | 3 +- src/view/com/composer/Composer.tsx | 21 - .../com/composer/videos/SelectVideoBtn.tsx | 112 ++--- src/view/com/feeds/FeedPage.tsx | 16 +- src/view/com/post-thread/PostThread.tsx | 18 +- src/view/com/post-thread/PostThreadItem.tsx | 4 +- src/view/com/post/Post.tsx | 16 +- src/view/com/posts/PostFeedItem.tsx | 4 +- src/view/com/util/post-ctrls/PostCtrls.tsx | 4 +- src/view/screens/Feeds.tsx | 15 +- src/view/screens/Lists.tsx | 37 +- src/view/screens/ModerationModlists.tsx | 37 +- src/view/screens/Notifications.tsx | 10 +- src/view/screens/Profile.tsx | 15 +- src/view/screens/ProfileList.tsx | 4 +- src/view/shell/desktop/LeftNav.tsx | 4 +- src/view/shell/index.tsx | 2 + src/view/shell/index.web.tsx | 2 + 55 files changed, 2257 insertions(+), 402 deletions(-) create mode 100644 src/components/dialogs/EmailDialog/components/ResendEmailText.tsx create mode 100644 src/components/dialogs/EmailDialog/components/TokenField.tsx create mode 100644 src/components/dialogs/EmailDialog/data/useAccountEmailState.ts create mode 100644 src/components/dialogs/EmailDialog/data/useConfirmEmail.ts create mode 100644 src/components/dialogs/EmailDialog/data/useManageEmail2FA.ts create mode 100644 src/components/dialogs/EmailDialog/data/useRequestEmailUpdate.ts create mode 100644 src/components/dialogs/EmailDialog/data/useRequestEmailVerification.ts create mode 100644 src/components/dialogs/EmailDialog/data/useUpdateEmail.ts create mode 100644 src/components/dialogs/EmailDialog/events.ts create mode 100644 src/components/dialogs/EmailDialog/index.tsx create mode 100644 src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx create mode 100644 src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx create mode 100644 src/components/dialogs/EmailDialog/screens/Manage2FA/index.tsx create mode 100644 src/components/dialogs/EmailDialog/screens/Update.tsx create mode 100644 src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx create mode 100644 src/components/dialogs/EmailDialog/screens/Verify.tsx create mode 100644 src/components/dialogs/EmailDialog/types.ts create mode 100644 src/lib/hooks/useCleanError.ts create mode 100644 src/lib/hooks/useOpenComposer.tsx create mode 100644 src/lib/hooks/useRequireEmailVerification.tsx diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 424d73290e..c983593877 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -104,8 +104,10 @@ import {Wizard} from '#/screens/StarterPack/Wizard' import TopicScreen from '#/screens/Topic' import {VideoFeed} from '#/screens/VideoFeed' import {useTheme} from '#/alf' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' +import { + EmailDialogScreenID, + useEmailDialogControl, +} from '#/components/dialogs/EmailDialog' import {router} from '#/routes' import {Referrer} from '../modules/expo-bluesky-swiss-army' @@ -738,12 +740,14 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) { const theme = useColorSchemeStyle(DefaultTheme, DarkTheme) const {currentAccount} = useSession() const prevLoggedRouteName = React.useRef(undefined) - const verifyEmailDialogControl = useDialogControl() + const emailDialogControl = useEmailDialogControl() function onReady() { prevLoggedRouteName.current = getCurrentRouteName() if (currentAccount && shouldRequestEmailConfirmation(currentAccount)) { - verifyEmailDialogControl.open() + emailDialogControl.open({ + id: EmailDialogScreenID.VerificationReminder, + }) snoozeEmailConfirmationPrompt() } } @@ -768,7 +772,6 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) { }}> {children} - ) } diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx index 12bd8819b1..c43e9c5c04 100644 --- a/src/components/Dialog/index.web.tsx +++ b/src/components/Dialog/index.web.tsx @@ -195,7 +195,13 @@ export function Inner({ onDismiss={close} style={{display: 'flex', flexDirection: 'column'}}> {header} - + {children} diff --git a/src/components/StarterPack/ProfileStarterPacks.tsx b/src/components/StarterPack/ProfileStarterPacks.tsx index d7afa37d25..de19b0bce8 100644 --- a/src/components/StarterPack/ProfileStarterPacks.tsx +++ b/src/components/StarterPack/ProfileStarterPacks.tsx @@ -18,7 +18,7 @@ import {useNavigation} from '@react-navigation/native' import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' -import {useEmail} from '#/lib/hooks/useEmail' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {type NavigationProp} from '#/lib/routes/types' import {parseStarterPackUri} from '#/lib/strings/starter-pack' @@ -30,7 +30,6 @@ import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {atoms as a, ios, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {LinearGradientBackground} from '#/components/LinearGradientBackground' import {Loader} from '#/components/Loader' @@ -197,9 +196,7 @@ function Empty() { const confirmDialogControl = useDialogControl() const followersDialogControl = useDialogControl() const errorDialogControl = useDialogControl() - - const {needsEmailVerification} = useEmail() - const verifyEmailControl = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() const [isGenerating, setIsGenerating] = useState(false) @@ -230,6 +227,27 @@ function Empty() { generateStarterPack() } + const openConfirmDialog = useCallback(() => { + confirmDialogControl.open() + }, [confirmDialogControl]) + const wrappedOpenConfirmDialog = requireEmailVerification(openConfirmDialog, { + instructions: [ + + Before creating a starter pack, you must first verify your email. + , + ], + }) + const navToWizard = useCallback(() => { + navigation.navigate('StarterPackWizard') + }, [navigation]) + const wrappedNavToWizard = requireEmailVerification(navToWizard, { + instructions: [ + + Before creating a starter pack, you must first verify your email. + , + ], + }) + return ( { - if (needsEmailVerification) { - verifyEmailControl.open() - } else { - confirmDialogControl.open() - } - }} + onPress={wrappedOpenConfirmDialog} style={{backgroundColor: 'transparent'}}> Make one for me @@ -277,13 +289,7 @@ function Empty() { color="primary" size="small" disabled={isGenerating} - onPress={() => { - if (needsEmailVerification) { - verifyEmailControl.open() - } else { - navigation.navigate('StarterPackWizard') - } - }} + onPress={wrappedNavToWizard} style={{ backgroundColor: 'white', borderColor: 'white', @@ -339,12 +345,6 @@ function Empty() { onConfirm={generate} confirmButtonCta={_(msg`Retry`)} /> - ) } diff --git a/src/components/dialogs/Context.tsx b/src/components/dialogs/Context.tsx index fda904b8b3..728044325b 100644 --- a/src/components/dialogs/Context.tsx +++ b/src/components/dialogs/Context.tsx @@ -1,6 +1,7 @@ import {createContext, useContext, useMemo, useState} from 'react' import * as Dialog from '#/components/Dialog' +import {type Screen} from '#/components/dialogs/EmailDialog/types' type Control = Dialog.DialogControlProps @@ -15,6 +16,7 @@ type ControlsContext = { mutedWordsDialogControl: Control signinDialogControl: Control inAppBrowserConsentControl: StatefulControl + emailDialogControl: StatefulControl } const ControlsContext = createContext(null) @@ -33,14 +35,21 @@ export function Provider({children}: React.PropsWithChildren<{}>) { const mutedWordsDialogControl = Dialog.useDialogControl() const signinDialogControl = Dialog.useDialogControl() const inAppBrowserConsentControl = useStatefulDialogControl() + const emailDialogControl = useStatefulDialogControl() const ctx = useMemo( () => ({ mutedWordsDialogControl, signinDialogControl, inAppBrowserConsentControl, + emailDialogControl, }), - [mutedWordsDialogControl, signinDialogControl, inAppBrowserConsentControl], + [ + mutedWordsDialogControl, + signinDialogControl, + inAppBrowserConsentControl, + emailDialogControl, + ], ) return ( @@ -48,7 +57,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) { ) } -function useStatefulDialogControl(initialValue?: T): StatefulControl { +export function useStatefulDialogControl( + initialValue?: T, +): StatefulControl { const [value, setValue] = useState(initialValue) const control = Dialog.useDialogControl() return useMemo( diff --git a/src/components/dialogs/EmailDialog/components/ResendEmailText.tsx b/src/components/dialogs/EmailDialog/components/ResendEmailText.tsx new file mode 100644 index 0000000000..0fc9a5469f --- /dev/null +++ b/src/components/dialogs/EmailDialog/components/ResendEmailText.tsx @@ -0,0 +1,56 @@ +import {useState} from 'react' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {wait} from '#/lib/async/wait' +import {atoms as a, type TextStyleProp, useTheme} from '#/alf' +import {CheckThick_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {createStaticClick, InlineLinkText} from '#/components/Link' +import {Loader} from '#/components/Loader' +import {Span, Text} from '#/components/Typography' + +export function ResendEmailText({ + onPress, + style, +}: TextStyleProp & { + onPress: () => Promise +}) { + const t = useTheme() + const {_} = useLingui() + const [status, setStatus] = useState<'sending' | 'success' | null>(null) + + const handleOnPress = async () => { + setStatus('sending') + try { + await wait(1000, onPress()) + setStatus('success') + } finally { + setTimeout(() => { + setStatus(null) + }, 1000) + } + } + + return ( + + + Don't see an email?{' '} + { + handleOnPress() + })}> + Click here to resend. + + {' '} + + {status === 'sending' ? ( + + ) : status === 'success' ? ( + + ) : null} + + + ) +} diff --git a/src/components/dialogs/EmailDialog/components/TokenField.tsx b/src/components/dialogs/EmailDialog/components/TokenField.tsx new file mode 100644 index 0000000000..26a51e4853 --- /dev/null +++ b/src/components/dialogs/EmailDialog/components/TokenField.tsx @@ -0,0 +1,45 @@ +import {type TextInputProps, View} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import * as TextField from '#/components/forms/TextField' +import {Shield_Stroke2_Corner0_Rounded as Shield} from '#/components/icons/Shield' + +export function normalizeCode(value: string) { + const normalized = value.toUpperCase().replace(/[^A-Z2-7]/g, '') + if (normalized.length <= 5) return normalized + return `${normalized.slice(0, 5)}-${normalized.slice(5)}` +} + +export function isValidCode(value?: string) { + return Boolean(value && /^[A-Z2-7]{5}-[A-Z2-7]{5}$/.test(value)) +} + +export function TokenField({ + value, + onChangeText, + onSubmitEditing, +}: Pick) { + const {_} = useLingui() + const isInvalid = Boolean(value && value.length > 10 && !isValidCode(value)) + + const handleOnChangeText = (v: string) => { + onChangeText?.(normalizeCode(v)) + } + + return ( + + + + + + + ) +} diff --git a/src/components/dialogs/EmailDialog/data/useAccountEmailState.ts b/src/components/dialogs/EmailDialog/data/useAccountEmailState.ts new file mode 100644 index 0000000000..377411107a --- /dev/null +++ b/src/components/dialogs/EmailDialog/data/useAccountEmailState.ts @@ -0,0 +1,79 @@ +import {useCallback, useEffect, useState} from 'react' +import {useQuery, useQueryClient} from '@tanstack/react-query' + +import {useAgent} from '#/state/session' +import {emitEmailVerified} from '#/components/dialogs/EmailDialog/events' + +export type AccountEmailState = { + isEmailVerified: boolean + email2FAEnabled: boolean +} + +export const accountEmailStateQueryKey = ['accountEmailState'] as const + +export function useInvalidateAccountEmailState() { + const qc = useQueryClient() + + return useCallback(() => { + return qc.invalidateQueries({ + queryKey: accountEmailStateQueryKey, + }) + }, [qc]) +} + +export function useUpdateAccountEmailStateQueryCache() { + const qc = useQueryClient() + + return useCallback( + (data: AccountEmailState) => { + return qc.setQueriesData( + { + queryKey: accountEmailStateQueryKey, + }, + data, + ) + }, + [qc], + ) +} + +export function useAccountEmailState() { + const agent = useAgent() + const [prevIsEmailVerified, setPrevEmailIsVerified] = useState( + !!agent.session?.emailConfirmed, + ) + const fallbackData: AccountEmailState = { + isEmailVerified: !!agent.session?.emailConfirmed, + email2FAEnabled: !!agent.session?.emailAuthFactor, + } + const query = useQuery({ + enabled: !!agent.session, + refetchOnWindowFocus: true, + queryKey: accountEmailStateQueryKey, + queryFn: async () => { + // will also trigger updates to `#/state/session` data + const {data} = await agent.resumeSession(agent.session!) + return { + isEmailVerified: !!data.emailConfirmed, + email2FAEnabled: !!data.emailAuthFactor, + } + }, + }) + + const state = query.data ?? fallbackData + + /* + * This will emit `n` times for each instance of this hook. So the listeners + * all use `once` to prevent multiple handlers firing. + */ + useEffect(() => { + if (state.isEmailVerified && !prevIsEmailVerified) { + setPrevEmailIsVerified(true) + emitEmailVerified() + } else if (!state.isEmailVerified && prevIsEmailVerified) { + setPrevEmailIsVerified(false) + } + }, [state, prevIsEmailVerified]) + + return state +} diff --git a/src/components/dialogs/EmailDialog/data/useConfirmEmail.ts b/src/components/dialogs/EmailDialog/data/useConfirmEmail.ts new file mode 100644 index 0000000000..73f824fcc6 --- /dev/null +++ b/src/components/dialogs/EmailDialog/data/useConfirmEmail.ts @@ -0,0 +1,29 @@ +import {useMutation} from '@tanstack/react-query' + +import {useAgent, useSession} from '#/state/session' +import {useUpdateAccountEmailStateQueryCache} from '#/components/dialogs/EmailDialog/data/useAccountEmailState' + +export function useConfirmEmail() { + const agent = useAgent() + const {currentAccount} = useSession() + const updateAccountEmailStateQueryCache = + useUpdateAccountEmailStateQueryCache() + + return useMutation({ + mutationFn: async ({token}: {token: string}) => { + if (!currentAccount?.email) { + throw new Error('No email found for the current account') + } + + await agent.com.atproto.server.confirmEmail({ + email: currentAccount.email, + token: token.trim(), + }) + const {data} = await agent.resumeSession(agent.session!) + updateAccountEmailStateQueryCache({ + isEmailVerified: !!data.emailConfirmed, + email2FAEnabled: !!data.emailAuthFactor, + }) + }, + }) +} diff --git a/src/components/dialogs/EmailDialog/data/useManageEmail2FA.ts b/src/components/dialogs/EmailDialog/data/useManageEmail2FA.ts new file mode 100644 index 0000000000..39f5fd2d9e --- /dev/null +++ b/src/components/dialogs/EmailDialog/data/useManageEmail2FA.ts @@ -0,0 +1,35 @@ +import {useMutation} from '@tanstack/react-query' + +import {useAgent, useSession} from '#/state/session' +import {useUpdateAccountEmailStateQueryCache} from '#/components/dialogs/EmailDialog/data/useAccountEmailState' + +export function useManageEmail2FA() { + const agent = useAgent() + const {currentAccount} = useSession() + const updateAccountEmailStateQueryCache = + useUpdateAccountEmailStateQueryCache() + + return useMutation({ + mutationFn: async ({ + enabled, + token, + }: + | {enabled: true; token?: undefined} + | {enabled: false; token: string}) => { + if (!currentAccount?.email) { + throw new Error('No email found for the current account') + } + + await agent.com.atproto.server.updateEmail({ + email: currentAccount.email, + emailAuthFactor: enabled, + token, + }) + const {data} = await agent.resumeSession(agent.session!) + updateAccountEmailStateQueryCache({ + isEmailVerified: !!data.emailConfirmed, + email2FAEnabled: !!data.emailAuthFactor, + }) + }, + }) +} diff --git a/src/components/dialogs/EmailDialog/data/useRequestEmailUpdate.ts b/src/components/dialogs/EmailDialog/data/useRequestEmailUpdate.ts new file mode 100644 index 0000000000..a442662fcf --- /dev/null +++ b/src/components/dialogs/EmailDialog/data/useRequestEmailUpdate.ts @@ -0,0 +1,13 @@ +import {useMutation} from '@tanstack/react-query' + +import {useAgent} from '#/state/session' + +export function useRequestEmailUpdate() { + const agent = useAgent() + + return useMutation({ + mutationFn: async () => { + return (await agent.com.atproto.server.requestEmailUpdate()).data + }, + }) +} diff --git a/src/components/dialogs/EmailDialog/data/useRequestEmailVerification.ts b/src/components/dialogs/EmailDialog/data/useRequestEmailVerification.ts new file mode 100644 index 0000000000..ae308c7afc --- /dev/null +++ b/src/components/dialogs/EmailDialog/data/useRequestEmailVerification.ts @@ -0,0 +1,13 @@ +import {useMutation} from '@tanstack/react-query' + +import {useAgent} from '#/state/session' + +export function useRequestEmailVerification() { + const agent = useAgent() + + return useMutation({ + mutationFn: async () => { + await agent.com.atproto.server.requestEmailConfirmation() + }, + }) +} diff --git a/src/components/dialogs/EmailDialog/data/useUpdateEmail.ts b/src/components/dialogs/EmailDialog/data/useUpdateEmail.ts new file mode 100644 index 0000000000..2ec1eb6dc2 --- /dev/null +++ b/src/components/dialogs/EmailDialog/data/useUpdateEmail.ts @@ -0,0 +1,45 @@ +import {useMutation} from '@tanstack/react-query' + +import {useAgent} from '#/state/session' +import {useRequestEmailUpdate} from '#/components/dialogs/EmailDialog/data/useRequestEmailUpdate' + +async function updateEmailAndRefreshSession( + agent: ReturnType, + email: string, + token?: string, +) { + await agent.com.atproto.server.updateEmail({email: email.trim(), token}) + await agent.resumeSession(agent.session!) +} + +export function useUpdateEmail() { + const agent = useAgent() + const {mutateAsync: requestEmailUpdate} = useRequestEmailUpdate() + + return useMutation< + {status: 'tokenRequired' | 'success'}, + Error, + {email: string; token?: string} + >({ + mutationFn: async ({email, token}: {email: string; token?: string}) => { + if (token) { + await updateEmailAndRefreshSession(agent, email, token) + return { + status: 'success', + } + } else { + const {tokenRequired} = await requestEmailUpdate() + if (tokenRequired) { + return { + status: 'tokenRequired', + } + } else { + await updateEmailAndRefreshSession(agent, email, token) + return { + status: 'success', + } + } + } + }, + }) +} diff --git a/src/components/dialogs/EmailDialog/events.ts b/src/components/dialogs/EmailDialog/events.ts new file mode 100644 index 0000000000..4fa171cad5 --- /dev/null +++ b/src/components/dialogs/EmailDialog/events.ts @@ -0,0 +1,23 @@ +import {useEffect} from 'react' +import EventEmitter from 'eventemitter3' + +const events = new EventEmitter<{ + emailVerified: void +}>() + +export function emitEmailVerified() { + events.emit('emailVerified') +} + +export function useOnEmailVerified(cb: () => void) { + useEffect(() => { + /* + * N.B. Use `once` here, since the event can fire multiple times for each + * instance of `useAccountEmailState` + */ + events.once('emailVerified', cb) + return () => { + events.off('emailVerified', cb) + } + }, [cb]) +} diff --git a/src/components/dialogs/EmailDialog/index.tsx b/src/components/dialogs/EmailDialog/index.tsx new file mode 100644 index 0000000000..4bac7295a5 --- /dev/null +++ b/src/components/dialogs/EmailDialog/index.tsx @@ -0,0 +1,71 @@ +import {useCallback, useState} from 'react' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import * as Dialog from '#/components/Dialog' +import {type StatefulControl} from '#/components/dialogs/Context' +import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' +import {useAccountEmailState} from '#/components/dialogs/EmailDialog/data/useAccountEmailState' +import {Manage2FA} from '#/components/dialogs/EmailDialog/screens/Manage2FA' +import {Update} from '#/components/dialogs/EmailDialog/screens/Update' +import {VerificationReminder} from '#/components/dialogs/EmailDialog/screens/VerificationReminder' +import {Verify} from '#/components/dialogs/EmailDialog/screens/Verify' +import {type Screen, ScreenID} from '#/components/dialogs/EmailDialog/types' + +export type {Screen} from '#/components/dialogs/EmailDialog/types' +export {ScreenID as EmailDialogScreenID} from '#/components/dialogs/EmailDialog/types' + +export function useEmailDialogControl() { + return useGlobalDialogsControlContext().emailDialogControl +} + +export function EmailDialog() { + const {_} = useLingui() + const emailDialogControl = useEmailDialogControl() + const {isEmailVerified} = useAccountEmailState() + const onClose = useCallback(() => { + if (!isEmailVerified) { + if (emailDialogControl.value?.id === ScreenID.Verify) { + emailDialogControl.value.onCloseWithoutVerifying?.() + } + } + emailDialogControl.clear() + }, [isEmailVerified, emailDialogControl]) + + return ( + + + + + + + + + ) +} + +function Inner({control}: {control: StatefulControl}) { + const [screen, showScreen] = useState(() => control.value) + + if (!screen) return null + + switch (screen.id) { + case ScreenID.Update: { + return + } + case ScreenID.Verify: { + return + } + case ScreenID.VerificationReminder: { + return + } + case ScreenID.Manage2FA: { + return + } + default: { + return null + } + } +} diff --git a/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx b/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx new file mode 100644 index 0000000000..1896ff27d7 --- /dev/null +++ b/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx @@ -0,0 +1,254 @@ +import {useReducer, useState} from 'react' +import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {wait} from '#/lib/async/wait' +import {useCleanError} from '#/lib/hooks/useCleanError' +import {logger} from '#/logger' +import {useSession} from '#/state/session' +import {atoms as a, useTheme} from '#/alf' +import {Admonition} from '#/components/Admonition' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {useDialogContext} from '#/components/Dialog' +import {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText' +import { + isValidCode, + TokenField, +} from '#/components/dialogs/EmailDialog/components/TokenField' +import {useManageEmail2FA} from '#/components/dialogs/EmailDialog/data/useManageEmail2FA' +import {useRequestEmailUpdate} from '#/components/dialogs/EmailDialog/data/useRequestEmailUpdate' +import {Divider} from '#/components/Divider' +import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {Envelope_Stroke2_Corner0_Rounded as Envelope} from '#/components/icons/Envelope' +import {createStaticClick, InlineLinkText} from '#/components/Link' +import {Loader} from '#/components/Loader' +import {Span, Text} from '#/components/Typography' + +type State = { + error: string + step: 'email' | 'token' + emailStatus: 'pending' | 'success' | 'error' | 'default' + tokenStatus: 'pending' | 'success' | 'error' | 'default' +} + +type Action = + | { + type: 'setError' + error: string + } + | { + type: 'setStep' + step: 'email' | 'token' + } + | { + type: 'setEmailStatus' + status: State['emailStatus'] + } + | { + type: 'setTokenStatus' + status: State['tokenStatus'] + } + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'setError': { + return { + ...state, + error: action.error, + emailStatus: 'error', + tokenStatus: 'error', + } + } + case 'setStep': { + return { + ...state, + error: '', + step: action.step, + } + } + case 'setEmailStatus': { + return { + ...state, + error: '', + emailStatus: action.status, + } + } + case 'setTokenStatus': { + return { + ...state, + error: '', + tokenStatus: action.status, + } + } + default: { + return state + } + } +} + +export function Disable() { + const t = useTheme() + const {_} = useLingui() + const cleanError = useCleanError() + const {currentAccount} = useSession() + const {mutateAsync: requestEmailUpdate} = useRequestEmailUpdate() + const {mutateAsync: manageEmail2FA} = useManageEmail2FA() + const control = useDialogContext() + + const [token, setToken] = useState('') + const [state, dispatch] = useReducer(reducer, { + error: '', + step: 'email', + emailStatus: 'default', + tokenStatus: 'default', + }) + + const handleSendEmail = async () => { + dispatch({type: 'setEmailStatus', status: 'pending'}) + try { + await wait(1000, requestEmailUpdate()) + dispatch({type: 'setEmailStatus', status: 'success'}) + setTimeout(() => { + dispatch({type: 'setStep', step: 'token'}) + }, 1000) + } catch (e) { + logger.error('Manage2FA: email update code request failed', { + safeMessage: e, + }) + const {clean} = cleanError(e) + dispatch({ + type: 'setError', + error: clean || _(msg`Failed to send email, please try again.`), + }) + } + } + + const handleManageEmail2FA = async () => { + if (!isValidCode(token)) { + dispatch({ + type: 'setError', + error: _(msg`Please enter a valid code.`), + }) + return + } + + dispatch({type: 'setTokenStatus', status: 'pending'}) + + try { + await wait(1000, manageEmail2FA({enabled: false, token})) + dispatch({type: 'setTokenStatus', status: 'success'}) + setTimeout(() => { + control.close() + }, 1000) + } catch (e) { + logger.error('Manage2FA: disable email 2FA failed', {safeMessage: e}) + const {clean} = cleanError(e) + dispatch({ + type: 'setError', + error: clean || _(msg`Failed to update email 2FA settings`), + }) + } + } + + return ( + + + Disable email 2FA + + + {state.step === 'email' ? ( + <> + + + To disable your email 2FA method, please verify your access to{' '} + {currentAccount?.email} + + + + + {state.error && {state.error}} + + + + + + + + Have a code?{' '} + { + dispatch({type: 'setStep', step: 'token'}) + })}> + Click here. + + + + + + ) : ( + <> + + + To disable your email 2FA method, please verify your access to{' '} + {currentAccount?.email} + + + + + + + + + {state.error && {state.error}} + + + + )} + + ) +} diff --git a/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx b/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx new file mode 100644 index 0000000000..7a126792a2 --- /dev/null +++ b/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx @@ -0,0 +1,137 @@ +import {useReducer} from 'react' +import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {wait} from '#/lib/async/wait' +import {useCleanError} from '#/lib/hooks/useCleanError' +import {logger} from '#/logger' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {Admonition} from '#/components/Admonition' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {useDialogContext} from '#/components/Dialog' +import {useManageEmail2FA} from '#/components/dialogs/EmailDialog/data/useManageEmail2FA' +import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {ShieldCheck_Stroke2_Corner0_Rounded as ShieldIcon} from '#/components/icons/Shield' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' + +type State = { + error: string + status: 'pending' | 'success' | 'error' | 'default' +} + +type Action = + | { + type: 'setError' + error: string + } + | { + type: 'setStatus' + status: State['status'] + } + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'setError': { + return { + ...state, + error: action.error, + status: 'error', + } + } + case 'setStatus': { + return { + ...state, + error: '', + status: action.status, + } + } + default: { + return state + } + } +} + +export function Enable() { + const t = useTheme() + const {_} = useLingui() + const cleanError = useCleanError() + const {gtPhone} = useBreakpoints() + const {mutateAsync: manageEmail2FA} = useManageEmail2FA() + const control = useDialogContext() + + const [state, dispatch] = useReducer(reducer, { + error: '', + status: 'default', + }) + + const handleManageEmail2FA = async () => { + dispatch({type: 'setStatus', status: 'pending'}) + + try { + await wait(1000, manageEmail2FA({enabled: true})) + dispatch({type: 'setStatus', status: 'success'}) + setTimeout(() => { + control.close() + }, 1000) + } catch (e) { + logger.error('Manage2FA: enable email 2FA failed', {safeMessage: e}) + const {clean} = cleanError(e) + dispatch({ + type: 'setError', + error: clean || _(msg`Failed to update email 2FA settings`), + }) + } + } + + return ( + + + + Enable email 2FA + + + + Require an email code to sign in to your account. + + + + {state.error && {state.error}} + + + + + + + ) +} diff --git a/src/components/dialogs/EmailDialog/screens/Manage2FA/index.tsx b/src/components/dialogs/EmailDialog/screens/Manage2FA/index.tsx new file mode 100644 index 0000000000..427a42b1fd --- /dev/null +++ b/src/components/dialogs/EmailDialog/screens/Manage2FA/index.tsx @@ -0,0 +1,70 @@ +import {useEffect, useState} from 'react' +import {Trans} from '@lingui/macro' + +import {useAccountEmailState} from '#/components/dialogs/EmailDialog/data/useAccountEmailState' +import {Disable} from '#/components/dialogs/EmailDialog/screens/Manage2FA/Disable' +import {Enable} from '#/components/dialogs/EmailDialog/screens/Manage2FA/Enable' +import { + ScreenID, + type ScreenProps, +} from '#/components/dialogs/EmailDialog/types' + +export function Manage2FA({showScreen}: ScreenProps) { + const {isEmailVerified, email2FAEnabled} = useAccountEmailState() + const [requestedAction, setRequestedAction] = useState< + 'enable' | 'disable' | null + >(null) + + useEffect(() => { + if (!isEmailVerified) { + showScreen({ + id: ScreenID.Verify, + instructions: [ + + You need to verify your email address before you can enable email + 2FA. + , + ], + onVerify: () => { + showScreen({ + id: ScreenID.Manage2FA, + }) + }, + }) + } + }, [isEmailVerified, showScreen]) + + /* + * Wacky state handling so that once 2FA settings change, we don't show the + * wrong step of this form - esb + */ + + if (email2FAEnabled) { + if (!requestedAction) { + setRequestedAction('disable') + return + } + + if (requestedAction === 'disable') { + return + } + if (requestedAction === 'enable') { + return + } + } else { + if (!requestedAction) { + setRequestedAction('enable') + return + } + + if (requestedAction === 'disable') { + return + } + if (requestedAction === 'enable') { + return + } + } + + // should never happen + return null +} diff --git a/src/components/dialogs/EmailDialog/screens/Update.tsx b/src/components/dialogs/EmailDialog/screens/Update.tsx new file mode 100644 index 0000000000..be0af88076 --- /dev/null +++ b/src/components/dialogs/EmailDialog/screens/Update.tsx @@ -0,0 +1,319 @@ +import {useReducer} from 'react' +import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {validate as validateEmail} from 'email-validator' + +import {wait} from '#/lib/async/wait' +import {useCleanError} from '#/lib/hooks/useCleanError' +import {logger} from '#/logger' +import {useSession} from '#/state/session' +import {atoms as a, useTheme} from '#/alf' +import {Admonition} from '#/components/Admonition' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText' +import { + isValidCode, + TokenField, +} from '#/components/dialogs/EmailDialog/components/TokenField' +import {useRequestEmailUpdate} from '#/components/dialogs/EmailDialog/data/useRequestEmailUpdate' +import {useRequestEmailVerification} from '#/components/dialogs/EmailDialog/data/useRequestEmailVerification' +import {useUpdateEmail} from '#/components/dialogs/EmailDialog/data/useUpdateEmail' +import { + type ScreenID, + type ScreenProps, +} from '#/components/dialogs/EmailDialog/types' +import {Divider} from '#/components/Divider' +import * as TextField from '#/components/forms/TextField' +import {CheckThick_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {Envelope_Stroke2_Corner0_Rounded as Envelope} from '#/components/icons/Envelope' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' + +type State = { + step: 'email' | 'token' + mutationStatus: 'pending' | 'success' | 'error' | 'default' + error: string + emailValid: boolean + email: string + token: string +} + +type Action = + | { + type: 'setStep' + step: State['step'] + } + | { + type: 'setError' + error: string + } + | { + type: 'setMutationStatus' + status: State['mutationStatus'] + } + | { + type: 'setEmail' + value: string + } + | { + type: 'setToken' + value: string + } + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'setStep': { + return { + ...state, + step: action.step, + } + } + case 'setError': { + return { + ...state, + error: action.error, + mutationStatus: 'error', + } + } + case 'setMutationStatus': { + return { + ...state, + error: '', + mutationStatus: action.status, + } + } + case 'setEmail': { + const emailValid = validateEmail(action.value) + return { + ...state, + step: 'email', + token: '', + email: action.value, + emailValid, + } + } + case 'setToken': { + return { + ...state, + error: '', + token: action.value, + } + } + } +} + +export function Update(_props: ScreenProps) { + const t = useTheme() + const {_} = useLingui() + const cleanError = useCleanError() + const {currentAccount} = useSession() + const [state, dispatch] = useReducer(reducer, { + step: 'email', + mutationStatus: 'default', + error: '', + email: '', + emailValid: true, + token: '', + }) + + const {mutateAsync: updateEmail} = useUpdateEmail() + const {mutateAsync: requestEmailUpdate} = useRequestEmailUpdate() + const {mutateAsync: requestEmailVerification} = useRequestEmailVerification() + + const handleEmailChange = (email: string) => { + dispatch({ + type: 'setEmail', + value: email, + }) + } + + const handleUpdateEmail = async () => { + if (state.step === 'token' && !isValidCode(state.token)) { + dispatch({ + type: 'setError', + error: _(msg`Please enter a valid code.`), + }) + return + } + + dispatch({ + type: 'setMutationStatus', + status: 'pending', + }) + + if (state.emailValid === false) { + dispatch({ + type: 'setError', + error: _(msg`Please enter a valid email address.`), + }) + return + } + + if (state.email === currentAccount!.email) { + dispatch({ + type: 'setError', + error: _(msg`This email is already associated with your account.`), + }) + return + } + + try { + const {status} = await wait( + 1000, + updateEmail({ + email: state.email, + token: state.token, + }), + ) + + if (status === 'tokenRequired') { + dispatch({ + type: 'setStep', + step: 'token', + }) + dispatch({ + type: 'setMutationStatus', + status: 'default', + }) + } else if (status === 'success') { + dispatch({ + type: 'setMutationStatus', + status: 'success', + }) + + try { + // fire off a confirmation email immediately + await requestEmailVerification() + } catch {} + } + } catch (e) { + logger.error('EmailDialog: update email failed', {safeMessage: e}) + const {clean} = cleanError(e) + dispatch({ + type: 'setError', + error: clean || _(msg`Failed to update email, please try again.`), + }) + } + } + + return ( + + + Update your email + + + {currentAccount?.emailAuthFactor && ( + + + If you update your email address, email 2FA will be disabled. + + + )} + + + + + Please enter your new email address. + + + + + + + + {state.step === 'token' && ( + <> + + + + Security step required + + + + Please enter the security code we sent to your previous email + address. + + + { + dispatch({ + type: 'setToken', + value: token, + }) + } + } + onSubmitEditing={handleUpdateEmail} + /> + {state.mutationStatus !== 'success' && ( + + )} + + + )} + + {state.error && {state.error}} + + + {state.mutationStatus === 'success' ? ( + <> + + + + + + Success! + + + + + Please click on the link in the email we just sent you to verify + your new email address. This is an important step to allow you + to continue enjoying all the features of Bluesky. + + + + + ) : ( + + )} + + ) +} diff --git a/src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx b/src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx new file mode 100644 index 0000000000..267b784b05 --- /dev/null +++ b/src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx @@ -0,0 +1,99 @@ +import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {atoms as a, platform, tokens, useBreakpoints, useTheme} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import {useDialogContext} from '#/components/Dialog' +import { + ScreenID, + type ScreenProps, +} from '#/components/dialogs/EmailDialog/types' +import {Divider} from '#/components/Divider' +import {GradientFill} from '#/components/GradientFill' +import {ShieldCheck_Stroke2_Corner0_Rounded as ShieldIcon} from '#/components/icons/Shield' +import {Text} from '#/components/Typography' + +export function VerificationReminder({ + showScreen, +}: ScreenProps) { + const t = useTheme() + const {_} = useLingui() + const {gtPhone, gtMobile} = useBreakpoints() + const control = useDialogContext() + + const dialogPadding = gtMobile ? a.p_2xl.padding : a.p_xl.padding + + return ( + + + + + + + + + + + + + Please verify your email + + + + Your email has not yet been verified. Please verify your email in + order to enjoy all the features of Bluesky. + + + + + + + + + + + + ) +} diff --git a/src/components/dialogs/EmailDialog/screens/Verify.tsx b/src/components/dialogs/EmailDialog/screens/Verify.tsx new file mode 100644 index 0000000000..dabd0d2f24 --- /dev/null +++ b/src/components/dialogs/EmailDialog/screens/Verify.tsx @@ -0,0 +1,386 @@ +import {useReducer} from 'react' +import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {wait} from '#/lib/async/wait' +import {useCleanError} from '#/lib/hooks/useCleanError' +import {logger} from '#/logger' +import {useSession} from '#/state/session' +import {atoms as a, useTheme} from '#/alf' +import {Admonition} from '#/components/Admonition' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText' +import { + isValidCode, + TokenField, +} from '#/components/dialogs/EmailDialog/components/TokenField' +import {useConfirmEmail} from '#/components/dialogs/EmailDialog/data/useConfirmEmail' +import {useRequestEmailVerification} from '#/components/dialogs/EmailDialog/data/useRequestEmailVerification' +import {useOnEmailVerified} from '#/components/dialogs/EmailDialog/events' +import { + ScreenID, + type ScreenProps, +} from '#/components/dialogs/EmailDialog/types' +import {Divider} from '#/components/Divider' +import {CheckThick_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {Envelope_Stroke2_Corner0_Rounded as Envelope} from '#/components/icons/Envelope' +import {createStaticClick, InlineLinkText} from '#/components/Link' +import {Loader} from '#/components/Loader' +import {Span, Text} from '#/components/Typography' + +type State = { + step: 'email' | 'token' | 'success' + mutationStatus: 'pending' | 'success' | 'error' | 'default' + error: string + token: string +} + +type Action = + | { + type: 'setStep' + step: State['step'] + } + | { + type: 'setError' + error: string + } + | { + type: 'setMutationStatus' + status: State['mutationStatus'] + } + | { + type: 'setToken' + value: string + } + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'setStep': { + return { + ...state, + error: '', + mutationStatus: 'default', + step: action.step, + } + } + case 'setError': { + return { + ...state, + error: action.error, + mutationStatus: 'error', + } + } + case 'setMutationStatus': { + return { + ...state, + error: '', + mutationStatus: action.status, + } + } + case 'setToken': { + return { + ...state, + error: '', + token: action.value, + } + } + } +} + +export function Verify({config, showScreen}: ScreenProps) { + const t = useTheme() + const {_} = useLingui() + const cleanError = useCleanError() + const {currentAccount} = useSession() + const [state, dispatch] = useReducer(reducer, { + step: 'email', + mutationStatus: 'default', + error: '', + token: '', + }) + + const {mutateAsync: requestEmailVerification} = useRequestEmailVerification() + const {mutateAsync: confirmEmail} = useConfirmEmail() + + useOnEmailVerified(() => { + if (config.onVerify) { + config.onVerify() + } else { + dispatch({ + type: 'setStep', + step: 'success', + }) + } + }) + + const handleRequestEmailVerification = async () => { + dispatch({ + type: 'setMutationStatus', + status: 'pending', + }) + + try { + await wait(1000, requestEmailVerification()) + dispatch({ + type: 'setMutationStatus', + status: 'success', + }) + } catch (e) { + logger.error('EmailDialog: sending verification email failed', { + safeMessage: e, + }) + const {clean} = cleanError(e) + dispatch({ + type: 'setError', + error: clean || _(msg`Failed to send email, please try again.`), + }) + } + } + + const handleConfirmEmail = async () => { + if (!isValidCode(state.token)) { + dispatch({ + type: 'setError', + error: _(msg`Please enter a valid code.`), + }) + return + } + + dispatch({ + type: 'setMutationStatus', + status: 'pending', + }) + + try { + await wait(1000, confirmEmail({token: state.token})) + dispatch({ + type: 'setStep', + step: 'success', + }) + } catch (e) { + logger.error('EmailDialog: confirming email failed', { + safeMessage: e, + }) + const {clean} = cleanError(e) + dispatch({ + type: 'setError', + error: clean || _(msg`Failed to verify email, please try again.`), + }) + } + } + + if (state.step === 'success') { + return ( + + + + + + + {' '} + Email verification complete! + + + + + You have successfully verified your email address. You can close + this dialog. + + + + + ) + } + + return ( + + + + {state.step === 'email' ? ( + state.mutationStatus === 'success' ? ( + <> + + + + {' '} + Email sent! + + ) : ( + Verify your email + ) + ) : ( + Verify email code + )} + + + {state.step === 'email' && state.mutationStatus !== 'success' && ( + <> + {config.instructions?.map((int, i) => ( + + {int} + + ))} + + )} + + + {state.step === 'email' ? ( + state.mutationStatus === 'success' ? ( + + We sent an email to{' '} + + {currentAccount!.email} + {' '} + containing a link. Please click on it to complete the email + verification process. + + ) : ( + + We'll send an email to{' '} + + {currentAccount!.email} + {' '} + containing a link. Please click on it to complete the email + verification process. + + ) + ) : ( + + Please enter the code we sent to{' '} + + {currentAccount!.email} + {' '} + below. + + )} + + + {state.step === 'email' && state.mutationStatus !== 'success' && ( + + + If you need to update your email,{' '} + { + showScreen({id: ScreenID.Update}) + })}> + click here + + . + + + )} + + {state.step === 'email' && state.mutationStatus === 'success' && ( + + )} + + + {state.step === 'email' && state.mutationStatus !== 'success' ? ( + <> + {state.error && {state.error}} + + + ) : null} + + {state.step === 'email' && ( + <> + + + + + Have a code?{' '} + { + dispatch({ + type: 'setStep', + step: 'token', + }) + })}> + Click here. + + + + + )} + + {state.step === 'token' ? ( + <> + { + dispatch({ + type: 'setToken', + value: token, + }) + }} + onSubmitEditing={handleConfirmEmail} + /> + + {state.error && {state.error}} + + + + + + + + Don't have a code or need a new one?{' '} + { + dispatch({ + type: 'setStep', + step: 'email', + }) + })}> + Click here. + + + + + ) : null} + + ) +} diff --git a/src/components/dialogs/EmailDialog/types.ts b/src/components/dialogs/EmailDialog/types.ts new file mode 100644 index 0000000000..7edc3facc6 --- /dev/null +++ b/src/components/dialogs/EmailDialog/types.ts @@ -0,0 +1,38 @@ +import {type ReactNode} from 'react' + +import {type DialogControlProps} from '#/components/Dialog' + +export type EmailDialogProps = { + control: DialogControlProps +} + +export type EmailDialogInnerProps = EmailDialogProps & {} + +export type Screen = + | { + id: ScreenID.Update + } + | { + id: ScreenID.Verify + instructions?: ReactNode[] + onVerify?: () => void + onCloseWithoutVerifying?: () => void + } + | { + id: ScreenID.VerificationReminder + } + | { + id: ScreenID.Manage2FA + } + +export enum ScreenID { + Update = 'Update', + Verify = 'Verify', + VerificationReminder = 'VerificationReminder', + Manage2FA = 'Manage2FA', +} + +export type ScreenProps = { + config: Extract + showScreen: (screen: Screen) => void +} diff --git a/src/components/dms/MessageProfileButton.tsx b/src/components/dms/MessageProfileButton.tsx index 7f31f550ca..07b3a0c042 100644 --- a/src/components/dms/MessageProfileButton.tsx +++ b/src/components/dms/MessageProfileButton.tsx @@ -1,20 +1,18 @@ import React from 'react' import {View} from 'react-native' -import {AppBskyActorDefs} from '@atproto/api' -import {msg} from '@lingui/macro' +import {type AppBskyActorDefs} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' -import {useEmail} from '#/lib/hooks/useEmail' -import {NavigationProp} from '#/lib/routes/types' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' +import {type NavigationProp} from '#/lib/routes/types' import {logEvent} from '#/lib/statsig/statsig' import {useGetConvoAvailabilityQuery} from '#/state/queries/messages/get-convo-availability' import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' import * as Toast from '#/view/com/util/Toast' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {canBeMessaged} from '#/components/dms/util' import {Message_Stroke2_Corner0_Rounded as Message} from '#/components/icons/Message' @@ -26,8 +24,7 @@ export function MessageProfileButton({ const {_} = useLingui() const t = useTheme() const navigation = useNavigation() - const {needsEmailVerification} = useEmail() - const verifyEmailControl = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() const {data: convoAvailability} = useGetConvoAvailabilityQuery(profile.did) const {mutate: initiateConvo} = useGetConvoForMembers({ @@ -45,11 +42,6 @@ export function MessageProfileButton({ return } - if (needsEmailVerification) { - verifyEmailControl.open() - return - } - if (convoAvailability.convo) { logEvent('chat:open', {logContext: 'ProfileHeader'}) navigation.navigate('MessagesConversation', { @@ -59,14 +51,15 @@ export function MessageProfileButton({ logEvent('chat:create', {logContext: 'ProfileHeader'}) initiateConvo([profile.did]) } - }, [ - needsEmailVerification, - verifyEmailControl, - navigation, - profile.did, - initiateConvo, - convoAvailability, - ]) + }, [navigation, profile.did, initiateConvo, convoAvailability]) + + const wrappedOnPress = requireEmailVerification(onPress, { + instructions: [ + + Before you can message another user, you must first verify your email. + , + ], + }) if (!convoAvailability) { // show pending state based on declaration @@ -102,15 +95,9 @@ export function MessageProfileButton({ shape="round" label={_(msg`Message ${profile.handle}`)} style={[a.justify_center]} - onPress={onPress}> + onPress={wrappedOnPress}> - ) } else { diff --git a/src/components/dms/dialogs/NewChatDialog.tsx b/src/components/dms/dialogs/NewChatDialog.tsx index a5ba793fb3..192e36b5d2 100644 --- a/src/components/dms/dialogs/NewChatDialog.tsx +++ b/src/components/dms/dialogs/NewChatDialog.tsx @@ -1,8 +1,8 @@ import {useCallback} from 'react' -import {msg} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useEmail} from '#/lib/hooks/useEmail' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {logEvent} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' @@ -10,9 +10,7 @@ import {FAB} from '#/view/com/util/fab/FAB' import * as Toast from '#/view/com/util/Toast' import {useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' -import {useDialogControl} from '#/components/Dialog' import {SearchablePeopleList} from '#/components/dialogs/SearchablePeopleList' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' export function NewChat({ @@ -24,8 +22,7 @@ export function NewChat({ }) { const t = useTheme() const {_} = useLingui() - const {needsEmailVerification} = useEmail() - const verifyEmailControl = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() const {mutate: createChat} = useGetConvoForMembers({ onSuccess: data => { @@ -49,17 +46,22 @@ export function NewChat({ [control, createChat], ) + const onPress = useCallback(() => { + control.open() + }, [control]) + const wrappedOnPress = requireEmailVerification(onPress, { + instructions: [ + + Before you can message another user, you must first verify your email. + , + ], + }) + return ( <> { - if (needsEmailVerification) { - verifyEmailControl.open() - } else { - control.open() - } - }} + onPress={wrappedOnPress} icon={} accessibilityRole="button" accessibilityLabel={_(msg`New chat`)} @@ -74,13 +76,6 @@ export function NewChat({ sortByMessageDeclaration /> - - ) } diff --git a/src/lib/hooks/useCleanError.ts b/src/lib/hooks/useCleanError.ts new file mode 100644 index 0000000000..dc9284e900 --- /dev/null +++ b/src/lib/hooks/useCleanError.ts @@ -0,0 +1,91 @@ +import {useCallback} from 'react' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +type CleanedError = { + raw: string | undefined + clean: string | undefined +} + +export function useCleanError() { + const {_} = useLingui() + + return useCallback<(error?: any) => CleanedError>( + error => { + if (!error) + return { + raw: undefined, + clean: undefined, + } + + let raw = error.toString() + + if (isNetworkError(raw)) { + return { + raw, + clean: _( + msg`Unable to connect. Please check your internet connection and try again.`, + ), + } + } + + if ( + raw.includes('Upstream Failure') || + raw.includes('NotEnoughResources') || + raw.includes('pipethrough network error') + ) { + return { + raw, + clean: _( + msg`The server appears to be experiencing issues. Please try again in a few moments.`, + ), + } + } + + if (raw.includes('Bad token scope')) { + return { + raw, + clean: _( + msg`This feature is not available while using an app password. Please sign in with your main password.`, + ), + } + } + + if (raw.includes('Rate Limit Exceeded')) { + return { + raw, + clean: _( + msg`You've reached the maximum number of requests allowed. Please try again later.`, + ), + } + } + + if (raw.startsWith('Error: ')) { + raw = raw.slice('Error: '.length) + } + + return { + raw, + clean: undefined, + } + }, + [_], + ) +} + +const NETWORK_ERRORS = [ + 'Abort', + 'Network request failed', + 'Failed to fetch', + 'Load failed', +] + +export function isNetworkError(e: unknown) { + const str = String(e) + for (const err of NETWORK_ERRORS) { + if (str.includes(err)) { + return true + } + } + return false +} diff --git a/src/lib/hooks/useIntentHandler.ts b/src/lib/hooks/useIntentHandler.ts index a33aff2371..4a56537500 100644 --- a/src/lib/hooks/useIntentHandler.ts +++ b/src/lib/hooks/useIntentHandler.ts @@ -1,10 +1,10 @@ import React from 'react' import * as Linking from 'expo-linking' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {logEvent} from '#/lib/statsig/statsig' import {isNative} from '#/platform/detection' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell' import {useCloseAllActiveElements} from '#/state/util' import {useIntentDialogs} from '#/components/intents/IntentDialogs' import {Referrer} from '../../../modules/expo-bluesky-swiss-army' @@ -83,7 +83,7 @@ export function useIntentHandler() { export function useComposeIntent() { const closeAllActiveElements = useCloseAllActiveElements() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const {hasSession} = useSession() return React.useCallback( diff --git a/src/lib/hooks/useOpenComposer.tsx b/src/lib/hooks/useOpenComposer.tsx new file mode 100644 index 0000000000..50c04d1e1f --- /dev/null +++ b/src/lib/hooks/useOpenComposer.tsx @@ -0,0 +1,22 @@ +import {useMemo} from 'react' +import {Trans} from '@lingui/macro' + +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' +import {useOpenComposer as rootUseOpenComposer} from '#/state/shell/composer' + +export function useOpenComposer() { + const {openComposer} = rootUseOpenComposer() + const requireEmailVerification = useRequireEmailVerification() + return useMemo(() => { + return { + openComposer: requireEmailVerification(openComposer, { + instructions: [ + + Before creating a post or replying, you must first verify your + email. + , + ], + }), + } + }, [openComposer, requireEmailVerification]) +} diff --git a/src/lib/hooks/useRequireEmailVerification.tsx b/src/lib/hooks/useRequireEmailVerification.tsx new file mode 100644 index 0000000000..26045847e0 --- /dev/null +++ b/src/lib/hooks/useRequireEmailVerification.tsx @@ -0,0 +1,53 @@ +import {useCallback} from 'react' +import {Keyboard} from 'react-native' + +import {useEmail} from '#/lib/hooks/useEmail' +import {useRequireAuth, useSession} from '#/state/session' +import {useCloseAllActiveElements} from '#/state/util' +import { + EmailDialogScreenID, + type Screen, + useEmailDialogControl, +} from '#/components/dialogs/EmailDialog' + +export function useRequireEmailVerification() { + const {currentAccount} = useSession() + const {needsEmailVerification} = useEmail() + const requireAuth = useRequireAuth() + const emailDialogControl = useEmailDialogControl() + const closeAll = useCloseAllActiveElements() + + return useCallback( + any>( + cb: T, + config: Omit< + Extract, + 'id' + > = {}, + ): ((...args: Parameters) => ReturnType) => { + return (...args: Parameters): ReturnType => { + if (!currentAccount) { + return requireAuth(() => cb(...args)) as ReturnType + } + if (needsEmailVerification) { + Keyboard.dismiss() + closeAll() + emailDialogControl.open({ + id: EmailDialogScreenID.Verify, + ...config, + }) + return undefined as ReturnType + } else { + return cb(...args) + } + } + }, + [ + needsEmailVerification, + currentAccount, + emailDialogControl, + closeAll, + requireAuth, + ], + ) +} diff --git a/src/screens/Messages/ChatList.tsx b/src/screens/Messages/ChatList.tsx index 2846ed828f..9fd54f1b04 100644 --- a/src/screens/Messages/ChatList.tsx +++ b/src/screens/Messages/ChatList.tsx @@ -9,6 +9,7 @@ import {type NativeStackScreenProps} from '@react-navigation/native-stack' import {useAppState} from '#/lib/hooks/useAppState' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {type MessagesTabNavigatorParams} from '#/lib/routes/types' import {cleanError} from '#/lib/strings/errors' import {logger} from '#/logger' @@ -321,6 +322,18 @@ export function MessagesScreen({navigation, route}: Props) { function Header({newChatControl}: {newChatControl: DialogControlProps}) { const {_} = useLingui() const {gtMobile} = useBreakpoints() + const requireEmailVerification = useRequireEmailVerification() + + const openChatControl = useCallback(() => { + newChatControl.open() + }, [newChatControl]) + const wrappedOpenChatControl = requireEmailVerification(openChatControl, { + instructions: [ + + Before you can message another user, you must first verify your email. + , + ], + }) const settingsLink = ( + onPress={wrappedOpenChatControl}> New chat diff --git a/src/screens/Messages/Conversation.tsx b/src/screens/Messages/Conversation.tsx index 2222084ce3..90547a8d4d 100644 --- a/src/screens/Messages/Conversation.tsx +++ b/src/screens/Messages/Conversation.tsx @@ -1,11 +1,11 @@ -import React, {useCallback} from 'react' +import React, {useCallback, useEffect} from 'react' import {View} from 'react-native' import { type AppBskyActorDefs, moderateProfile, type ModerationDecision, } from '@atproto/api' -import {msg} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import { type RouteProp, @@ -17,6 +17,7 @@ import {type NativeStackScreenProps} from '@react-navigation/native-stack' import {useEmail} from '#/lib/hooks/useEmail' import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController' +import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import { type CommonNavigatorParams, type NavigationProp, @@ -31,8 +32,10 @@ import {useProfileQuery} from '#/state/queries/profile' import {useSetMinimalShellMode} from '#/state/shell' import {MessagesList} from '#/screens/Messages/components/MessagesList' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' +import { + EmailDialogScreenID, + useEmailDialogControl, +} from '#/components/dialogs/EmailDialog' import {MessagesListBlockedFooter} from '#/components/dms/MessagesListBlockedFooter' import {MessagesListHeader} from '#/components/dms/MessagesListHeader' import {Error} from '#/components/Error' @@ -183,19 +186,50 @@ function InnerReady({ hasScrolled: boolean setHasScrolled: React.Dispatch> }) { - const {_} = useLingui() const convoState = useConvo() const navigation = useNavigation() const {params} = useRoute>() - const verifyEmailControl = useDialogControl() const {needsEmailVerification} = useEmail() + const emailDialogControl = useEmailDialogControl() - React.useEffect(() => { + /** + * Must be non-reactive, otherwise the update to open the global dialog will + * cause a re-render loop. + */ + const maybeBlockForEmailVerification = useNonReactiveCallback(() => { if (needsEmailVerification) { - verifyEmailControl.open() + /* + * HACKFIX + * + * Load bearing timeout, to bump this state update until the after the + * `navigator.addListener('state')` handler closes elements from + * `shell/index.*.tsx` - sfn & esb + */ + setTimeout(() => + emailDialogControl.open({ + id: EmailDialogScreenID.Verify, + instructions: [ + + Before you can message another user, you must first verify your + email. + , + ], + onCloseWithoutVerifying: () => { + if (navigation.canGoBack()) { + navigation.goBack() + } else { + navigation.navigate('Messages', {animation: 'pop'}) + } + }, + }), + ) } - }, [needsEmailVerification, verifyEmailControl]) + }) + + useEffect(() => { + maybeBlockForEmailVerification() + }, [maybeBlockForEmailVerification]) return ( <> @@ -216,15 +250,6 @@ function InnerReady({ } /> )} - { - navigation.navigate('Home') - }} - /> ) } diff --git a/src/screens/Messages/components/RequestButtons.tsx b/src/screens/Messages/components/RequestButtons.tsx index 62db096009..3490bec0da 100644 --- a/src/screens/Messages/components/RequestButtons.tsx +++ b/src/screens/Messages/components/RequestButtons.tsx @@ -1,11 +1,12 @@ import {useCallback} from 'react' -import {ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api' +import {type ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {StackActions, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' -import {NavigationProp} from '#/lib/routes/types' +import {useEmail} from '#/lib/hooks/useEmail' +import {type NavigationProp} from '#/lib/routes/types' import {useProfileShadow} from '#/state/cache/profile-shadow' import {useAcceptConversation} from '#/state/queries/messages/accept-conversation' import {precacheConvoQuery} from '#/state/queries/messages/conversation' @@ -13,8 +14,17 @@ import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useProfileBlockMutationQueue} from '#/state/queries/profile' import * as Toast from '#/view/com/util/Toast' import {atoms as a} from '#/alf' -import {Button, ButtonIcon, ButtonProps, ButtonText} from '#/components/Button' +import { + Button, + ButtonIcon, + type ButtonProps, + ButtonText, +} from '#/components/Button' import {useDialogControl} from '#/components/Dialog' +import { + EmailDialogScreenID, + useEmailDialogControl, +} from '#/components/dialogs/EmailDialog' import {ReportDialog} from '#/components/dms/ReportDialog' import {CircleX_Stroke2_Corner0_Rounded} from '#/components/icons/CircleX' import {Flag_Stroke2_Corner0_Rounded as FlagIcon} from '#/components/icons/Flag' @@ -186,6 +196,8 @@ export function AcceptChatButton({ const {_} = useLingui() const queryClient = useQueryClient() const navigation = useNavigation() + const {needsEmailVerification} = useEmail() + const emailDialogControl = useEmailDialogControl() const {mutate: acceptConvo, isPending} = useAcceptConversation(convo.id, { onMutate: () => { @@ -216,8 +228,20 @@ export function AcceptChatButton({ }) const onPressAccept = useCallback(() => { - acceptConvo() - }, [acceptConvo]) + if (needsEmailVerification) { + emailDialogControl.open({ + id: EmailDialogScreenID.Verify, + instructions: [ + + Before you can accept this chat request, you must first verify your + email. + , + ], + }) + } else { + acceptConvo() + } + }, [acceptConvo, needsEmailVerification, emailDialogControl]) return ( - - - ) -} - -function VerifyEmailPrompt({control}: {control: Prompt.PromptControlProps}) { - const {_} = useLingui() - const verifyEmailDialogControl = useDialogControl() - - return ( - <> - { - verifyEmailDialogControl.open() - }} - /> - ) } diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index f643adaf9f..604533b0f1 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -1,32 +1,32 @@ import React from 'react' import {View} from 'react-native' -import {AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api' +import {type AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {NavigationProp, useNavigation} from '@react-navigation/native' +import {type NavigationProp, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import {VIDEO_FEED_URIS} from '#/lib/constants' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {ComposeIcon2} from '#/lib/icons' import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers' -import {AllNavigatorParams} from '#/lib/routes/types' +import {type AllNavigatorParams} from '#/lib/routes/types' import {logEvent} from '#/lib/statsig/statsig' import {s} from '#/lib/styles' import {isNative} from '#/platform/detection' import {listenSoftReset} from '#/state/events' import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback' import {useSetHomeBadge} from '#/state/home-badge' -import {SavedFeedSourceInfo} from '#/state/queries/feed' +import {type SavedFeedSourceInfo} from '#/state/queries/feed' import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' -import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed' +import {type FeedDescriptor, type FeedParams} from '#/state/queries/post-feed' import {truncateAndInvalidate} from '#/state/queries/util' import {useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {useHeaderOffset} from '#/components/hooks/useHeaderOffset' import {PostFeed} from '../posts/PostFeed' import {FAB} from '../util/fab/FAB' -import {ListMethods} from '../util/List' +import {type ListMethods} from '../util/List' import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn' import {MainScrollProvider} from '../util/MainScrollProvider' @@ -57,7 +57,7 @@ export function FeedPage({ const {_} = useLingui() const navigation = useNavigation>() const queryClient = useQueryClient() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const [isScrolledDown, setIsScrolledDown] = React.useState(false) const setMinimalShellMode = useSetMinimalShellMode() const headerOffset = useHeaderOffset() diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index 83dbdb5537..d974ce6b53 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -5,7 +5,7 @@ import Animated from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' import { AppBskyFeedDefs, - AppBskyFeedThreadgate, + type AppBskyFeedThreadgate, moderatePost, } from '@atproto/api' import {msg, Trans} from '@lingui/macro' @@ -14,6 +14,7 @@ import {useLingui} from '@lingui/react' import {HITSLOP_10} from '#/lib/constants' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useMinimalShellFabTransform} from '#/lib/hooks/useMinimalShellTransform' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useSetTitle} from '#/lib/hooks/useSetTitle' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {clamp} from '#/lib/numbers' @@ -25,19 +26,18 @@ import {useModerationOpts} from '#/state/preferences/moderation-opts' import { fillThreadModerationCache, sortThread, - ThreadBlocked, - ThreadModerationCache, - ThreadNode, - ThreadNotFound, - ThreadPost, + type ThreadBlocked, + type ThreadModerationCache, + type ThreadNode, + type ThreadNotFound, + type ThreadPost, usePostThreadQuery, } from '#/state/queries/post-thread' import {useSetThreadViewPreferencesMutation} from '#/state/queries/preferences' import {usePreferencesQuery} from '#/state/queries/preferences' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' -import {List, ListMethods} from '#/view/com/util/List' +import {List, type ListMethods} from '#/view/com/util/List' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider' @@ -394,7 +394,7 @@ export function PostThread({uri}: {uri: string | undefined}) { [refetch], ) - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const onPressReply = React.useCallback(() => { if (thread?.type !== 'post') { return diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index dfd641f666..10c3e6b4d1 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -17,6 +17,7 @@ import {msg, Plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {MAX_POST_LINES} from '#/lib/constants' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenLink} from '#/lib/hooks/useOpenLink' import {usePalette} from '#/lib/hooks/usePalette' import {makeProfileLink} from '#/lib/routes/links' @@ -36,7 +37,6 @@ import {useProfileShadow} from '#/state/cache/profile-shadow' import {useLanguagePrefs} from '#/state/preferences' import {type ThreadPost} from '#/state/queries/post-thread' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {PostThreadFollowBtn} from '#/view/com/post-thread/PostThreadFollowBtn' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' @@ -204,7 +204,7 @@ let PostThreadItemLoaded = ({ const pal = usePalette('default') const {_, i18n} = useLingui() const langPrefs = useLanguagePrefs() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const [limitLines, setLimitLines] = React.useState( () => countLines(richText?.text) >= MAX_POST_LINES, ) diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx index 600cee4289..c6cf254f34 100644 --- a/src/view/com/post/Post.tsx +++ b/src/view/com/post/Post.tsx @@ -1,11 +1,11 @@ import React, {useMemo, useState} from 'react' -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' +import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native' import { - AppBskyFeedDefs, + type AppBskyFeedDefs, AppBskyFeedPost, AtUri, moderatePost, - ModerationDecision, + type ModerationDecision, RichText as RichTextAPI, } from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' @@ -14,15 +14,19 @@ import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' import {MAX_POST_LINES} from '#/lib/constants' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {makeProfileLink} from '#/lib/routes/links' import {countLines} from '#/lib/strings/helpers' import {colors, s} from '#/lib/styles' -import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow' +import { + POST_TOMBSTONE, + type Shadow, + usePostShadow, +} from '#/state/cache/post-shadow' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {precacheProfile} from '#/state/queries/profile' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import {AviFollowButton} from '#/view/com/posts/AviFollowButton' import {atoms as a} from '#/alf' import {ProfileHoverCard} from '#/components/ProfileHoverCard' @@ -113,7 +117,7 @@ function PostInner({ const queryClient = useQueryClient() const pal = usePalette('default') const {_} = useLingui() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const [limitLines, setLimitLines] = useState( () => countLines(richText?.text) >= MAX_POST_LINES, ) diff --git a/src/view/com/posts/PostFeedItem.tsx b/src/view/com/posts/PostFeedItem.tsx index facd31e5fc..123a8b0c29 100644 --- a/src/view/com/posts/PostFeedItem.tsx +++ b/src/view/com/posts/PostFeedItem.tsx @@ -19,6 +19,7 @@ import {useQueryClient} from '@tanstack/react-query' import {isReasonFeedSource, type ReasonFeedSource} from '#/lib/api/feed/types' import {MAX_POST_LINES} from '#/lib/constants' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {makeProfileLink} from '#/lib/routes/links' import {sanitizeDisplayName} from '#/lib/strings/display-names' @@ -33,7 +34,6 @@ import { import {useFeedFeedbackContext} from '#/state/feed-feedback' import {precacheProfile} from '#/state/queries/profile' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {FeedNameText} from '#/view/com/util/FeedInfoText' import {PostCtrls} from '#/view/com/util/post-ctrls/PostCtrls' @@ -159,7 +159,7 @@ let FeedItemInner = ({ onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void }): React.ReactNode => { const queryClient = useQueryClient() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const pal = usePalette('default') const {_} = useLingui() diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx index d97654a633..a9cae8886a 100644 --- a/src/view/com/util/post-ctrls/PostCtrls.tsx +++ b/src/view/com/util/post-ctrls/PostCtrls.tsx @@ -22,6 +22,7 @@ import {DISCOVER_DEBUG_DIDS, POST_CTRL_HITSLOP} from '#/lib/constants' import {CountWheel} from '#/lib/custom-animations/CountWheel' import {AnimatedLikeIcon} from '#/lib/custom-animations/LikeIcon' import {useHaptics} from '#/lib/haptics' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {makeProfileLink} from '#/lib/routes/links' import {shareUrl} from '#/lib/sharing' import {useGate} from '#/lib/statsig/statsig' @@ -33,7 +34,6 @@ import { usePostRepostMutationQueue, } from '#/state/queries/post' import {useRequireAuth, useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import { ProgressGuideAction, useProgressGuideControls, @@ -76,7 +76,7 @@ let PostCtrls = ({ }): React.ReactNode => { const t = useTheme() const {_, i18n} = useLingui() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const {currentAccount} = useSession() const [queueLike, queueUnlike] = usePostLikeMutationQueue(post, logContext) const [queueRepost, queueUnrepost] = usePostRepostMutationQueue( diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index 520400fd17..ca0280116d 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -1,30 +1,33 @@ import React from 'react' import {ActivityIndicator, StyleSheet, View} from 'react-native' -import {AppBskyFeedDefs} from '@atproto/api' +import {type AppBskyFeedDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' import debounce from 'lodash.debounce' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {ComposeIcon2} from '#/lib/icons' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' import {cleanError} from '#/lib/strings/errors' import {s} from '#/lib/styles' import {isNative, isWeb} from '#/platform/detection' import { - SavedFeedItem, + type SavedFeedItem, useGetPopularFeedsQuery, useSavedFeeds, useSearchPopularFeedsMutation, } from '#/state/queries/feed' import {useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' import {FAB} from '#/view/com/util/fab/FAB' -import {List, ListMethods} from '#/view/com/util/List' +import {List, type ListMethods} from '#/view/com/util/List' import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {Text} from '#/view/com/util/text/Text' import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed' @@ -102,7 +105,7 @@ type FlatlistSlice = export function FeedsScreen(_props: Props) { const pal = usePalette('default') - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const {isMobile} = useWebMediaQueries() const [query, setQuery] = React.useState('') const [isPTR, setIsPTR] = React.useState(false) diff --git a/src/view/screens/Lists.tsx b/src/view/screens/Lists.tsx index 300153966d..bcda97dc5a 100644 --- a/src/view/screens/Lists.tsx +++ b/src/view/screens/Lists.tsx @@ -4,16 +4,17 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' -import {useEmail} from '#/lib/hooks/useEmail' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' -import {NavigationProp} from '#/lib/routes/types' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {type NavigationProp} from '#/lib/routes/types' import {useModalControls} from '#/state/modals' import {useSetMinimalShellMode} from '#/state/shell' import {MyLists} from '#/view/com/lists/MyLists' import {atoms as a} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus' import * as Layout from '#/components/Layout' @@ -23,8 +24,7 @@ export function ListsScreen({}: Props) { const setMinimalShellMode = useSetMinimalShellMode() const navigation = useNavigation() const {openModal} = useModalControls() - const {needsEmailVerification} = useEmail() - const control = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() useFocusEffect( React.useCallback(() => { @@ -33,11 +33,6 @@ export function ListsScreen({}: Props) { ) const onPressNewList = React.useCallback(() => { - if (needsEmailVerification) { - control.open() - return - } - openModal({ name: 'create-or-edit-list', purpose: 'app.bsky.graph.defs#curatelist', @@ -51,7 +46,15 @@ export function ListsScreen({}: Props) { } catch {} }, }) - }, [needsEmailVerification, control, openModal, navigation]) + }, [openModal, navigation]) + + const wrappedOnPressNewList = requireEmailVerification(onPressNewList, { + instructions: [ + + Before creating a list, you must first verify your email. + , + ], + }) return ( @@ -68,7 +71,7 @@ export function ListsScreen({}: Props) { color="secondary" variant="solid" size="small" - onPress={onPressNewList}> + onPress={wrappedOnPressNewList}> New @@ -76,12 +79,6 @@ export function ListsScreen({}: Props) { - ) } diff --git a/src/view/screens/ModerationModlists.tsx b/src/view/screens/ModerationModlists.tsx index 0b5090e3d4..23ed492f64 100644 --- a/src/view/screens/ModerationModlists.tsx +++ b/src/view/screens/ModerationModlists.tsx @@ -4,16 +4,17 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' -import {useEmail} from '#/lib/hooks/useEmail' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' -import {NavigationProp} from '#/lib/routes/types' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {type NavigationProp} from '#/lib/routes/types' import {useModalControls} from '#/state/modals' import {useSetMinimalShellMode} from '#/state/shell' import {MyLists} from '#/view/com/lists/MyLists' import {atoms as a} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus' import * as Layout from '#/components/Layout' @@ -23,8 +24,7 @@ export function ModerationModlistsScreen({}: Props) { const setMinimalShellMode = useSetMinimalShellMode() const navigation = useNavigation() const {openModal} = useModalControls() - const {needsEmailVerification} = useEmail() - const control = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() useFocusEffect( React.useCallback(() => { @@ -33,11 +33,6 @@ export function ModerationModlistsScreen({}: Props) { ) const onPressNewList = React.useCallback(() => { - if (needsEmailVerification) { - control.open() - return - } - openModal({ name: 'create-or-edit-list', purpose: 'app.bsky.graph.defs#modlist', @@ -51,7 +46,15 @@ export function ModerationModlistsScreen({}: Props) { } catch {} }, }) - }, [needsEmailVerification, control, openModal, navigation]) + }, [openModal, navigation]) + + const wrappedOnPressNewList = requireEmailVerification(onPressNewList, { + instructions: [ + + Before creating a list, you must first verify your email. + , + ], + }) return ( @@ -68,7 +71,7 @@ export function ModerationModlistsScreen({}: Props) { color="secondary" variant="solid" size="small" - onPress={onPressNewList}> + onPress={wrappedOnPressNewList}> New @@ -76,12 +79,6 @@ export function ModerationModlistsScreen({}: Props) { - ) } diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 1880fb816a..ace0de2aed 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -6,10 +6,11 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {ComposeIcon2} from '#/lib/icons' import { - NativeStackScreenProps, - NotificationsTabNavigatorParams, + type NativeStackScreenProps, + type NotificationsTabNavigatorParams, } from '#/lib/routes/types' import {s} from '#/lib/styles' import {logger} from '#/logger' @@ -22,12 +23,11 @@ import { } from '#/state/queries/notifications/unread' import {truncateAndInvalidate} from '#/state/queries/util' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {NotificationFeed} from '#/view/com/notifications/NotificationFeed' import {Pager} from '#/view/com/pager/Pager' import {TabBar} from '#/view/com/pager/TabBar' import {FAB} from '#/view/com/util/fab/FAB' -import {ListMethods} from '#/view/com/util/List' +import {type ListMethods} from '#/view/com/util/List' import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn' import {MainScrollProvider} from '#/view/com/util/MainScrollProvider' import {atoms as a} from '#/alf' @@ -49,7 +49,7 @@ type Props = NativeStackScreenProps< > export function NotificationsScreen({}: Props) { const {_} = useLingui() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const unreadNotifs = useUnreadNotifications() const hasNew = !!unreadNotifs const {checkUnread: checkUnreadAll} = useUnreadNotificationsApi() diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 425d55656c..cc339bb03d 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -2,9 +2,9 @@ import React, {useCallback, useMemo} from 'react' import {StyleSheet} from 'react-native' import {SafeAreaView} from 'react-native-safe-area-context' import { - AppBskyActorDefs, + type AppBskyActorDefs, moderateProfile, - ModerationOpts, + type ModerationOpts, RichText as RichTextAPI, } from '@atproto/api' import {msg} from '@lingui/macro' @@ -12,9 +12,13 @@ import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useSetTitle} from '#/lib/hooks/useSetTitle' import {ComposeIcon2} from '#/lib/icons' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' import {combinedDisplayName} from '#/lib/strings/display-names' import {cleanError} from '#/lib/strings/errors' import {isInvalidHandle} from '#/lib/strings/handles' @@ -28,13 +32,12 @@ import {useProfileQuery} from '#/state/queries/profile' import {useResolveDidQuery} from '#/state/queries/resolve-uri' import {useAgent, useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens' import {ProfileLists} from '#/view/com/lists/ProfileLists' import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader' import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' import {FAB} from '#/view/com/util/fab/FAB' -import {ListRef} from '#/view/com/util/List' +import {type ListRef} from '#/view/com/util/List' import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header' import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed' import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels' @@ -165,7 +168,7 @@ function ProfileScreenLoaded({ const profile = useProfileShadow(profileUnshadowed) const {hasSession, currentAccount} = useSession() const setMinimalShellMode = useSetMinimalShellMode() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const { data: labelerInfo, error: labelerError, diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index 61f1eb745a..78cf5d11e7 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -16,6 +16,7 @@ import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import {useHaptics} from '#/lib/haptics' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {useSetTitle} from '#/lib/hooks/useSetTitle' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' @@ -54,7 +55,6 @@ import {useResolveUriQuery} from '#/state/queries/resolve-uri' import {truncateAndInvalidate} from '#/state/queries/util' import {useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {ListMembers} from '#/view/com/lists/ListMembers' import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader' import {PostFeed} from '#/view/com/posts/PostFeed' @@ -155,7 +155,7 @@ function ProfileListScreenLoaded({ }) { const {_} = useLingui() const queryClient = useQueryClient() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const setMinimalShellMode = useSetMinimalShellMode() const {currentAccount} = useSession() const {rkey} = route.params diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 0688fb5dc4..7d34a3d14f 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -10,6 +10,7 @@ import { } from '@react-navigation/native' import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {getCurrentRoute, isTab} from '#/lib/routes/helpers' @@ -25,7 +26,6 @@ import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useProfilesQuery} from '#/state/queries/profile' import {type SessionAccount, useSession, useSessionApi} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useCloseAllActiveElements} from '#/state/util' import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' @@ -447,7 +447,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) { function ComposeBtn() { const {currentAccount} = useSession() const {getState} = useNavigation() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const {_} = useLingui() const {leftNavMinimal} = useLayoutBreakpoints() const [isFetchingHandle, setIsFetchingHandle] = React.useState(false) diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index 1e34f6da53..cd328c4572 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -25,6 +25,7 @@ import {ModalsContainer} from '#/view/com/modals/Modal' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {atoms as a, select, useTheme} from '#/alf' import {setSystemUITheme} from '#/alf/util/systemUI' +import {EmailDialog} from '#/components/dialogs/EmailDialog' import {InAppBrowserConsentDialog} from '#/components/dialogs/InAppBrowserConsent' import {MutedWordsDialog} from '#/components/dialogs/MutedWords' import {SigninDialog} from '#/components/dialogs/Signin' @@ -152,6 +153,7 @@ function ShellInner() { + diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index 898ff8fa83..a7ff76d61b 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -17,6 +17,7 @@ import {Lightbox} from '#/view/com/lightbox/Lightbox' import {ModalsContainer} from '#/view/com/modals/Modal' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {atoms as a, select, useTheme} from '#/alf' +import {EmailDialog} from '#/components/dialogs/EmailDialog' import {MutedWordsDialog} from '#/components/dialogs/MutedWords' import {SigninDialog} from '#/components/dialogs/Signin' import {Outlet as PortalOutlet} from '#/components/Portal' @@ -67,6 +68,7 @@ function ShellInner() { + From f1372abec5943076d0cd0409db04c6b1587cd241 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 7 May 2025 21:44:38 +0300 Subject: [PATCH 6/9] delete unused auth components (#8334) --- src/view/com/auth/util/HelpTip.tsx | 37 ------------------ src/view/com/auth/util/TextInput.tsx | 56 ---------------------------- 2 files changed, 93 deletions(-) delete mode 100644 src/view/com/auth/util/HelpTip.tsx delete mode 100644 src/view/com/auth/util/TextInput.tsx diff --git a/src/view/com/auth/util/HelpTip.tsx b/src/view/com/auth/util/HelpTip.tsx deleted file mode 100644 index 196f30412b..0000000000 --- a/src/view/com/auth/util/HelpTip.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import {StyleSheet, View} from 'react-native' - -import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' -import {InfoCircleIcon} from '#/lib/icons' -import {colors, s} from '#/lib/styles' -import {Text} from '#/view/com/util/text/Text' - -export function HelpTip({text}: {text: string}) { - const bg = useColorSchemeStyle( - {backgroundColor: colors.gray1}, - {backgroundColor: colors.gray8}, - ) - const fg = useColorSchemeStyle({color: colors.gray5}, {color: colors.gray4}) - return ( - - - - - - {text} - - - ) -} - -const styles = StyleSheet.create({ - icon: { - width: 18, - }, - helptip: { - flexDirection: 'row', - alignItems: 'flex-start', - borderRadius: 6, - paddingHorizontal: 10, - paddingVertical: 8, - }, -}) diff --git a/src/view/com/auth/util/TextInput.tsx b/src/view/com/auth/util/TextInput.tsx deleted file mode 100644 index 083dda555a..0000000000 --- a/src/view/com/auth/util/TextInput.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import {ComponentProps} from 'react' -import {StyleSheet, TextInput as RNTextInput, View} from 'react-native' -import {IconProp} from '@fortawesome/fontawesome-svg-core' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' - -import {usePalette} from '#/lib/hooks/usePalette' -import {useTheme} from '#/lib/ThemeContext' - -interface Props extends Omit, 'onChange'> { - testID?: string - icon: IconProp - onChange: (v: string) => void -} - -export function TextInput({testID, icon, onChange, ...props}: Props) { - const theme = useTheme() - const pal = usePalette('default') - return ( - - - onChange(v)} - {...props} - /> - - ) -} - -const styles = StyleSheet.create({ - container: { - borderWidth: 1, - borderRadius: 6, - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 4, - }, - icon: { - marginLeft: 10, - }, - textInput: { - flex: 1, - width: '100%', - paddingVertical: 10, - paddingHorizontal: 10, - fontSize: 17, - letterSpacing: 0.25, - fontWeight: '400', - borderRadius: 10, - }, -}) From 0b06a93833b861f6402653bce60a6fd473a62290 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 7 May 2025 21:49:49 +0300 Subject: [PATCH 7/9] add 10px more padding (#8345) --- src/components/Dialog/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx index f9712f7797..4c6c5816c5 100644 --- a/src/components/Dialog/index.tsx +++ b/src/components/Dialog/index.tsx @@ -298,9 +298,7 @@ export const InnerFlatList = React.forwardRef< - } + ListFooterComponent={} ref={ref} {...props} style={[style]} From a260ecd3075fd46a92d88089423c55c7a843366d Mon Sep 17 00:00:00 2001 From: pfrazee <1270099+pfrazee@users.noreply.github.com> Date: Thu, 8 May 2025 02:39:40 +0000 Subject: [PATCH 8/9] Nightly source-language update --- src/locale/locales/en/messages.po | 730 ++++++++++++++++++------------ 1 file changed, 451 insertions(+), 279 deletions(-) diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index e5e0cd20b6..35249eac35 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -22,7 +22,7 @@ msgstr "" msgid "(contains embedded content)" msgstr "" -#: src/screens/Settings/AccountSettings.tsx:67 +#: src/screens/Settings/AccountSettings.tsx:68 msgid "(no email)" msgstr "" @@ -445,7 +445,7 @@ msgstr "" msgid "A new form of verification" msgstr "" -#: src/Navigation.tsx:401 +#: src/Navigation.tsx:403 #: src/screens/Settings/AboutSettings.tsx:75 #: src/screens/Settings/Settings.tsx:224 #: src/screens/Settings/Settings.tsx:227 @@ -453,11 +453,11 @@ msgid "About" msgstr "" #. Accept a chat request -#: src/screens/Messages/components/RequestButtons.tsx:235 +#: src/screens/Messages/components/RequestButtons.tsx:259 msgid "Accept" msgstr "" -#: src/screens/Messages/components/RequestButtons.tsx:225 +#: src/screens/Messages/components/RequestButtons.tsx:249 msgid "Accept chat request" msgstr "" @@ -472,19 +472,19 @@ msgstr "" msgid "Accessibility" msgstr "" -#: src/Navigation.tsx:353 +#: src/Navigation.tsx:355 msgid "Accessibility Settings" msgstr "" -#: src/Navigation.tsx:369 +#: src/Navigation.tsx:371 #: src/screens/Login/LoginForm.tsx:194 -#: src/screens/Settings/AccountSettings.tsx:47 +#: src/screens/Settings/AccountSettings.tsx:48 #: src/screens/Settings/Settings.tsx:162 #: src/screens/Settings/Settings.tsx:165 msgid "Account" msgstr "" -#: src/screens/Messages/components/RequestButtons.tsx:81 +#: src/screens/Messages/components/RequestButtons.tsx:91 #: src/view/com/profile/ProfileMenu.tsx:147 #: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:404 msgctxt "toast" @@ -590,7 +590,7 @@ msgstr "" msgid "Add another account" msgstr "" -#: src/view/com/composer/Composer.tsx:752 +#: src/view/com/composer/Composer.tsx:731 msgid "Add another post" msgstr "" @@ -621,7 +621,7 @@ msgstr "" msgid "Add muted words and tags" msgstr "" -#: src/view/com/composer/Composer.tsx:1308 +#: src/view/com/composer/Composer.tsx:1287 msgid "Add new post" msgstr "" @@ -713,6 +713,7 @@ msgid "Advanced" msgstr "" #: src/components/dialogs/ChangeEmailDialog.tsx:143 +#: src/components/dialogs/EmailDialog/screens/Update.tsx:223 msgid "alice@example.com" msgstr "" @@ -730,7 +731,7 @@ msgstr "" msgid "All languages" msgstr "" -#: src/view/screens/Feeds.tsx:702 +#: src/view/screens/Feeds.tsx:705 msgid "All the feeds you've saved, right in one place." msgstr "" @@ -822,7 +823,7 @@ msgstr "" msgid "An error occurred while fetching the feed." msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:337 +#: src/components/StarterPack/ProfileStarterPacks.tsx:343 msgid "An error occurred while generating your starter pack. Want to try again?" msgstr "" @@ -839,7 +840,7 @@ msgstr "" msgid "An error occurred while saving the QR code!" msgstr "" -#: src/view/com/composer/videos/SelectVideoBtn.tsx:83 +#: src/view/com/composer/videos/SelectVideoBtn.tsx:63 msgid "An error occurred while selecting the video" msgstr "" @@ -862,7 +863,7 @@ msgstr "" msgid "An issue not included in these options" msgstr "" -#: src/components/dms/dialogs/NewChatDialog.tsx:41 +#: src/components/dms/dialogs/NewChatDialog.tsx:38 msgid "An issue occurred starting the chat" msgstr "" @@ -918,7 +919,7 @@ msgstr "" msgid "Anybody can interact" msgstr "" -#: src/Navigation.tsx:409 +#: src/Navigation.tsx:411 #: src/screens/Settings/AppIconSettings/index.tsx:67 #: src/screens/Settings/AppIconSettings/SettingsListItem.tsx:18 #: src/screens/Settings/AppIconSettings/SettingsListItem.tsx:23 @@ -955,7 +956,7 @@ msgstr "" msgid "App passwords" msgstr "" -#: src/Navigation.tsx:321 +#: src/Navigation.tsx:323 #: src/screens/Settings/AppPasswords.tsx:51 msgid "App Passwords" msgstr "" @@ -991,7 +992,7 @@ msgstr "" msgid "Appeal this decision" msgstr "" -#: src/Navigation.tsx:361 +#: src/Navigation.tsx:363 #: src/screens/Settings/AppearanceSettings.tsx:85 #: src/screens/Settings/Settings.tsx:192 #: src/screens/Settings/Settings.tsx:195 @@ -1044,11 +1045,11 @@ msgstr "" msgid "Are you sure you want to remove this from your feeds?" msgstr "" -#: src/view/com/composer/Composer.tsx:703 +#: src/view/com/composer/Composer.tsx:682 msgid "Are you sure you'd like to discard this draft?" msgstr "" -#: src/view/com/composer/Composer.tsx:882 +#: src/view/com/composer/Composer.tsx:861 msgid "Are you sure you'd like to discard this post?" msgstr "" @@ -1110,27 +1111,33 @@ msgstr "" msgid "Back to Chats" msgstr "" -#: src/view/screens/Lists.tsx:81 -#: src/view/screens/ModerationModlists.tsx:81 +#: src/view/screens/Lists.tsx:53 +#: src/view/screens/ModerationModlists.tsx:53 msgid "Before creating a list, you must first verify your email." msgstr "" -#: src/view/com/composer/Composer.tsx:629 -msgid "Before creating a post, you must first verify your email." +#: src/lib/hooks/useOpenComposer.tsx:14 +msgid "Before creating a post or replying, you must first verify your email." msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:344 +#: src/components/StarterPack/ProfileStarterPacks.tsx:235 +#: src/components/StarterPack/ProfileStarterPacks.tsx:245 msgid "Before creating a starter pack, you must first verify your email." msgstr "" -#: src/components/dms/dialogs/NewChatDialog.tsx:80 -#: src/components/dms/MessageProfileButton.tsx:110 -#: src/screens/Messages/Conversation.tsx:221 -msgid "Before you may message another user, you must first verify your email." +#: src/screens/Messages/components/RequestButtons.tsx:235 +msgid "Before you can accept this chat request, you must first verify your email." +msgstr "" + +#: src/components/dms/dialogs/NewChatDialog.tsx:54 +#: src/components/dms/MessageProfileButton.tsx:58 +#: src/screens/Messages/ChatList.tsx:332 +#: src/screens/Messages/Conversation.tsx:213 +msgid "Before you can message another user, you must first verify your email." msgstr "" #: src/components/dialogs/BirthDateSettings.tsx:103 -#: src/screens/Settings/AccountSettings.tsx:111 +#: src/screens/Settings/AccountSettings.tsx:120 msgid "Birthday" msgstr "" @@ -1142,8 +1149,8 @@ msgstr "" #: src/components/dms/ConvoMenu.tsx:247 #: src/components/dms/ConvoMenu.tsx:250 -#: src/screens/Messages/components/RequestButtons.tsx:134 -#: src/screens/Messages/components/RequestButtons.tsx:136 +#: src/screens/Messages/components/RequestButtons.tsx:144 +#: src/screens/Messages/components/RequestButtons.tsx:146 #: src/view/com/profile/ProfileMenu.tsx:346 #: src/view/com/profile/ProfileMenu.tsx:353 #: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:718 @@ -1197,7 +1204,7 @@ msgstr "" msgid "Blocked accounts" msgstr "" -#: src/Navigation.tsx:162 +#: src/Navigation.tsx:164 #: src/view/screens/ModerationBlockedAccounts.tsx:104 msgid "Blocked Accounts" msgstr "" @@ -1262,7 +1269,7 @@ msgstr "" msgid "Bluesky Social Terms of Service" msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:304 +#: src/components/StarterPack/ProfileStarterPacks.tsx:310 msgid "Bluesky will choose a set of recommended accounts from people in your network." msgstr "" @@ -1375,6 +1382,8 @@ msgstr "" msgid "Camera" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx:125 +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx:131 #: src/components/dialogs/InAppBrowserConsent.tsx:98 #: src/components/dialogs/InAppBrowserConsent.tsx:104 #: src/components/Menu/index.tsx:306 @@ -1391,8 +1400,8 @@ msgstr "" #: src/screens/Settings/Settings.tsx:269 #: src/screens/Takendown.tsx:99 #: src/screens/Takendown.tsx:102 -#: src/view/com/composer/Composer.tsx:937 -#: src/view/com/composer/Composer.tsx:948 +#: src/view/com/composer/Composer.tsx:916 +#: src/view/com/composer/Composer.tsx:927 #: src/view/com/composer/photos/EditImageDialog.web.tsx:43 #: src/view/com/composer/photos/EditImageDialog.web.tsx:52 #: src/view/com/modals/ChangePassword.tsx:279 @@ -1459,7 +1468,7 @@ msgstr "" msgid "Captions & alt text" msgstr "" -#: src/screens/Settings/components/Email2FAToggle.tsx:76 +#: src/screens/Settings/components/Email2FAToggle.tsx:31 msgid "Change" msgstr "" @@ -1476,11 +1485,6 @@ msgstr "" msgid "Change app language" msgstr "" -#: src/screens/Settings/AccountSettings.tsx:99 -#: src/screens/Settings/AccountSettings.tsx:103 -msgid "Change email" -msgstr "" - #: src/components/dialogs/VerifyEmailDialog.tsx:200 #: src/components/dialogs/VerifyEmailDialog.tsx:225 msgid "Change email address" @@ -1524,15 +1528,15 @@ msgstr "" msgid "Changes hosting provider" msgstr "" -#: src/Navigation.tsx:426 +#: src/Navigation.tsx:428 #: src/view/shell/bottom-bar/BottomBar.tsx:209 #: src/view/shell/desktop/LeftNav.tsx:535 #: src/view/shell/Drawer.tsx:438 msgid "Chat" msgstr "" -#: src/screens/Messages/components/RequestButtons.tsx:68 -#: src/screens/Messages/components/RequestButtons.tsx:280 +#: src/screens/Messages/components/RequestButtons.tsx:78 +#: src/screens/Messages/components/RequestButtons.tsx:304 msgctxt "toast" msgid "Chat deleted" msgstr "" @@ -1542,7 +1546,7 @@ msgctxt "toast" msgid "Chat muted" msgstr "" -#: src/Navigation.tsx:436 +#: src/Navigation.tsx:438 #: src/screens/Messages/components/InboxPreview.tsx:24 msgid "Chat request inbox" msgstr "" @@ -1553,8 +1557,8 @@ msgid "Chat requests" msgstr "" #: src/components/dms/ConvoMenu.tsx:75 -#: src/Navigation.tsx:431 -#: src/screens/Messages/ChatList.tsx:328 +#: src/Navigation.tsx:433 +#: src/screens/Messages/ChatList.tsx:341 msgid "Chat settings" msgstr "" @@ -1567,8 +1571,8 @@ msgctxt "toast" msgid "Chat unmuted" msgstr "" -#: src/screens/Messages/ChatList.tsx:344 -#: src/screens/Messages/ChatList.tsx:368 +#: src/screens/Messages/ChatList.tsx:357 +#: src/screens/Messages/ChatList.tsx:381 msgid "Chats" msgstr "" @@ -1593,7 +1597,7 @@ msgstr "" msgid "Choose Feeds" msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:312 +#: src/components/StarterPack/ProfileStarterPacks.tsx:318 msgid "Choose for me" msgstr "" @@ -1613,7 +1617,7 @@ msgstr "" msgid "Choose your account provider" msgstr "" -#: src/view/screens/Feeds.tsx:728 +#: src/view/screens/Feeds.tsx:731 msgid "Choose your own timeline! Feeds built by the community help you find content you love." msgstr "" @@ -1646,6 +1650,14 @@ msgstr "" msgid "click here" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:371 +msgid "Click here to restart the verification process." +msgstr "" + +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:273 +msgid "Click here to update your email" +msgstr "" + #: src/components/dialogs/PostInteractionSettingsDialog.tsx:337 msgid "Click to disable quote posts of this post." msgstr "" @@ -1695,7 +1707,7 @@ msgid "Close" msgstr "" #: src/components/Dialog/index.web.tsx:111 -#: src/components/Dialog/index.web.tsx:259 +#: src/components/Dialog/index.web.tsx:265 msgid "Close active dialog" msgstr "" @@ -1713,7 +1725,7 @@ msgstr "" msgid "Close dialog" msgstr "" -#: src/view/shell/index.web.tsx:83 +#: src/view/shell/index.web.tsx:85 msgid "Close drawer menu" msgstr "" @@ -1748,7 +1760,7 @@ msgstr "" msgid "Closes password update alert" msgstr "" -#: src/view/com/composer/Composer.tsx:945 +#: src/view/com/composer/Composer.tsx:924 msgid "Closes post composer and discards post draft" msgstr "" @@ -1788,7 +1800,7 @@ msgstr "" msgid "Comics" msgstr "" -#: src/Navigation.tsx:311 +#: src/Navigation.tsx:313 #: src/view/screens/CommunityGuidelines.tsx:34 msgid "Community Guidelines" msgstr "" @@ -1805,7 +1817,7 @@ msgstr "" msgid "Compose new post" msgstr "" -#: src/view/com/composer/Composer.tsx:846 +#: src/view/com/composer/Composer.tsx:825 msgid "Compose posts up to {0, plural, other {# characters}} in length" msgstr "" @@ -1813,7 +1825,7 @@ msgstr "" msgid "Compose reply" msgstr "" -#: src/view/com/composer/Composer.tsx:1690 +#: src/view/com/composer/Composer.tsx:1669 msgid "Compressing video..." msgstr "" @@ -1854,6 +1866,7 @@ msgstr "" #: src/components/dialogs/ChangeEmailDialog.tsx:160 #: src/components/dialogs/ChangeEmailDialog.tsx:164 +#: src/components/dialogs/EmailDialog/components/TokenField.tsx:36 #: src/components/dialogs/VerifyEmailDialog.tsx:252 #: src/screens/Login/LoginForm.tsx:274 #: src/screens/Settings/components/DisableEmail2FADialog.tsx:144 @@ -1886,7 +1899,7 @@ msgstr "" msgid "Content and media" msgstr "" -#: src/Navigation.tsx:385 +#: src/Navigation.tsx:387 msgid "Content and Media" msgstr "" @@ -2059,7 +2072,7 @@ msgstr "" msgid "Copy TXT record value" msgstr "" -#: src/Navigation.tsx:316 +#: src/Navigation.tsx:318 #: src/view/screens/CopyrightPolicy.tsx:31 msgid "Copyright Policy" msgstr "" @@ -2069,7 +2082,7 @@ msgstr "" msgid "Could not leave chat" msgstr "" -#: src/screens/Profile/ProfileFeed/index.tsx:80 +#: src/screens/Profile/ProfileFeed/index.tsx:83 msgid "Could not load feed" msgstr "" @@ -2085,7 +2098,7 @@ msgstr "" msgid "Could not process your video" msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:294 +#: src/components/StarterPack/ProfileStarterPacks.tsx:300 msgid "Create" msgstr "" @@ -2093,13 +2106,13 @@ msgstr "" msgid "Create a QR code for a starter pack" msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:179 -#: src/components/StarterPack/ProfileStarterPacks.tsx:275 -#: src/Navigation.tsx:461 +#: src/components/StarterPack/ProfileStarterPacks.tsx:178 +#: src/components/StarterPack/ProfileStarterPacks.tsx:287 +#: src/Navigation.tsx:463 msgid "Create a starter pack" msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:256 +#: src/components/StarterPack/ProfileStarterPacks.tsx:274 msgid "Create a starter pack for me" msgstr "" @@ -2132,7 +2145,7 @@ msgstr "" msgid "Create an avatar instead" msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:186 +#: src/components/StarterPack/ProfileStarterPacks.tsx:185 msgid "Create another" msgstr "" @@ -2200,8 +2213,8 @@ msgstr "" msgid "Date of birth" msgstr "" -#: src/screens/Settings/AccountSettings.tsx:148 -#: src/screens/Settings/AccountSettings.tsx:153 +#: src/screens/Settings/AccountSettings.tsx:157 +#: src/screens/Settings/AccountSettings.tsx:162 #: src/screens/Settings/components/DeactivateAccountDialog.tsx:73 msgid "Deactivate account" msgstr "" @@ -2233,8 +2246,8 @@ msgstr "" msgid "Delete" msgstr "" -#: src/screens/Settings/AccountSettings.tsx:158 -#: src/screens/Settings/AccountSettings.tsx:163 +#: src/screens/Settings/AccountSettings.tsx:167 +#: src/screens/Settings/AccountSettings.tsx:172 msgid "Delete account" msgstr "" @@ -2250,8 +2263,8 @@ msgstr "" msgid "Delete app password?" msgstr "" -#: src/screens/Messages/components/RequestButtons.tsx:292 -#: src/screens/Messages/components/RequestButtons.tsx:299 +#: src/screens/Messages/components/RequestButtons.tsx:316 +#: src/screens/Messages/components/RequestButtons.tsx:323 msgid "Delete chat" msgstr "" @@ -2261,8 +2274,8 @@ msgstr "" #: src/components/dms/ReportDialog.tsx:362 #: src/components/dms/ReportDialog.tsx:365 -#: src/screens/Messages/components/RequestButtons.tsx:126 -#: src/screens/Messages/components/RequestButtons.tsx:129 +#: src/screens/Messages/components/RequestButtons.tsx:136 +#: src/screens/Messages/components/RequestButtons.tsx:139 msgid "Delete conversation" msgstr "" @@ -2290,7 +2303,7 @@ msgstr "" msgid "Delete my account" msgstr "" -#: src/view/com/composer/Composer.tsx:856 +#: src/view/com/composer/Composer.tsx:835 #: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:758 #: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:760 msgid "Delete post" @@ -2379,6 +2392,15 @@ msgstr "" msgid "Dim" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:233 +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:242 +msgid "Disable 2FA" +msgstr "" + +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:157 +msgid "Disable email 2FA" +msgstr "" + #: src/screens/Settings/components/DisableEmail2FADialog.tsx:89 msgid "Disable Email 2FA" msgstr "" @@ -2402,8 +2424,8 @@ msgid "Disabled" msgstr "" #: src/screens/Profile/Header/EditProfileDialog.tsx:88 -#: src/view/com/composer/Composer.tsx:705 -#: src/view/com/composer/Composer.tsx:889 +#: src/view/com/composer/Composer.tsx:684 +#: src/view/com/composer/Composer.tsx:868 msgid "Discard" msgstr "" @@ -2411,11 +2433,11 @@ msgstr "" msgid "Discard changes?" msgstr "" -#: src/view/com/composer/Composer.tsx:702 +#: src/view/com/composer/Composer.tsx:681 msgid "Discard draft?" msgstr "" -#: src/view/com/composer/Composer.tsx:881 +#: src/view/com/composer/Composer.tsx:860 msgid "Discard post?" msgstr "" @@ -2433,15 +2455,15 @@ msgstr "" msgid "Discover new custom feeds" msgstr "" -#: src/view/screens/Feeds.tsx:725 +#: src/view/screens/Feeds.tsx:728 msgid "Discover New Feeds" msgstr "" -#: src/components/Dialog/index.tsx:323 +#: src/components/Dialog/index.tsx:321 msgid "Dismiss" msgstr "" -#: src/view/com/composer/Composer.tsx:1614 +#: src/view/com/composer/Composer.tsx:1593 msgid "Dismiss error" msgstr "" @@ -2502,6 +2524,14 @@ msgstr "" msgid "Domain verified!" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:368 +msgid "Don't have a code or need a new one? <0>Click here." +msgstr "" + +#: src/components/dialogs/EmailDialog/components/ResendEmailText.tsx:37 +msgid "Don't see an email? <0>Click here to resend." +msgstr "" + #: src/components/dialogs/BirthDateSettings.tsx:115 #: src/components/dialogs/BirthDateSettings.tsx:121 #: src/components/dms/ReportDialog.tsx:314 @@ -2539,11 +2569,11 @@ msgstr "" msgid "Double tap or long press the message to add a reaction" msgstr "" -#: src/components/Dialog/index.tsx:324 +#: src/components/Dialog/index.tsx:322 msgid "Double tap to close the dialog" msgstr "" -#: src/screens/VideoFeed/index.tsx:1049 +#: src/screens/VideoFeed/index.tsx:1057 msgid "Double tap to like" msgstr "" @@ -2608,7 +2638,7 @@ msgstr "" msgid "Each code works once. You'll receive more invite codes periodically." msgstr "" -#: src/screens/Settings/AccountSettings.tsx:114 +#: src/screens/Settings/AccountSettings.tsx:123 #: src/screens/StarterPack/StarterPackScreen.tsx:583 #: src/screens/StarterPack/Wizard/index.tsx:534 #: src/screens/StarterPack/Wizard/index.tsx:541 @@ -2654,8 +2684,8 @@ msgstr "" msgid "Edit Moderation List" msgstr "" -#: src/Navigation.tsx:326 -#: src/view/screens/Feeds.tsx:515 +#: src/Navigation.tsx:328 +#: src/view/screens/Feeds.tsx:518 msgid "Edit My Feeds" msgstr "" @@ -2704,7 +2734,7 @@ msgstr "" msgid "Edit your profile description" msgstr "" -#: src/Navigation.tsx:466 +#: src/Navigation.tsx:468 msgid "Edit your starter pack" msgstr "" @@ -2717,7 +2747,7 @@ msgstr "" msgid "Either the creator of this list has blocked you or you have blocked the creator." msgstr "" -#: src/screens/Settings/AccountSettings.tsx:62 +#: src/screens/Settings/AccountSettings.tsx:63 #: src/screens/Signup/StepInfo/index.tsx:193 msgid "Email" msgstr "" @@ -2739,10 +2769,18 @@ msgstr "" msgid "Email Resent" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:208 +msgid "Email sent!" +msgstr "" + #: src/components/dialogs/ChangeEmailDialog.tsx:63 msgid "Email Updated!" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:182 +msgid "Email verification complete!" +msgstr "" + #: src/components/intents/VerifyEmailIntentDialog.tsx:79 msgid "Email Verified" msgstr "" @@ -2766,8 +2804,9 @@ msgstr "" msgid "Embedded video player" msgstr "" -#: src/screens/Settings/components/Email2FAToggle.tsx:60 -#: src/screens/Settings/components/Email2FAToggle.tsx:76 +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx:104 +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx:111 +#: src/screens/Settings/components/Email2FAToggle.tsx:31 msgid "Enable" msgstr "" @@ -2779,8 +2818,8 @@ msgstr "" msgid "Enable adult content" msgstr "" -#: src/screens/Settings/components/Email2FAToggle.tsx:57 -msgid "Enable Email 2FA" +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx:92 +msgid "Enable email 2FA" msgstr "" #: src/components/dialogs/EmbedConsent.tsx:81 @@ -2841,6 +2880,11 @@ msgstr "" msgid "Enter a word or tag" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:201 +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:318 +msgid "Enter code" +msgstr "" + #: src/components/dialogs/VerifyEmailDialog.tsx:118 msgid "Enter Code" msgstr "" @@ -2894,7 +2938,7 @@ msgstr "" msgid "Entertainment" msgstr "" -#: src/view/com/composer/Composer.tsx:1699 +#: src/view/com/composer/Composer.tsx:1678 #: src/view/com/util/error/ErrorScreen.tsx:42 msgid "Error" msgstr "" @@ -2978,7 +3022,7 @@ msgstr "" msgid "Expand or collapse the full post you are replying to" msgstr "" -#: src/screens/VideoFeed/index.tsx:943 +#: src/screens/VideoFeed/index.tsx:951 msgid "Expands or collapses post text" msgstr "" @@ -3013,15 +3057,15 @@ msgstr "" msgid "Explicit sexual images." msgstr "" -#: src/Navigation.tsx:650 +#: src/Navigation.tsx:652 #: src/screens/Search/Shell.tsx:307 #: src/view/shell/desktop/LeftNav.tsx:617 #: src/view/shell/Drawer.tsx:386 msgid "Explore" msgstr "" -#: src/screens/Settings/AccountSettings.tsx:139 -#: src/screens/Settings/AccountSettings.tsx:143 +#: src/screens/Settings/AccountSettings.tsx:148 +#: src/screens/Settings/AccountSettings.tsx:152 msgid "Export my data" msgstr "" @@ -3044,12 +3088,12 @@ msgstr "" msgid "External media may allow websites to collect information about you and your device. No information is sent or requested until you press the \"play\" button." msgstr "" -#: src/Navigation.tsx:345 +#: src/Navigation.tsx:347 #: src/screens/Settings/ExternalMediaPreferences.tsx:31 msgid "External Media Preferences" msgstr "" -#: src/screens/Messages/components/RequestButtons.tsx:208 +#: src/screens/Messages/components/RequestButtons.tsx:220 msgctxt "toast" msgid "Failed to accept chat" msgstr "" @@ -3067,7 +3111,7 @@ msgstr "" msgid "Failed to create app password. Please try again." msgstr "" -#: src/components/dms/MessageProfileButton.tsx:39 +#: src/components/dms/MessageProfileButton.tsx:36 msgid "Failed to create conversation" msgstr "" @@ -3080,8 +3124,8 @@ msgstr "" msgid "Failed to create the list. Check your internet connection and try again." msgstr "" -#: src/screens/Messages/components/RequestButtons.tsx:54 -#: src/screens/Messages/components/RequestButtons.tsx:267 +#: src/screens/Messages/components/RequestButtons.tsx:64 +#: src/screens/Messages/components/RequestButtons.tsx:291 msgctxt "toast" msgid "Failed to delete chat" msgstr "" @@ -3098,7 +3142,7 @@ msgstr "" msgid "Failed to delete starter pack" msgstr "" -#: src/screens/Messages/ChatList.tsx:243 +#: src/screens/Messages/ChatList.tsx:244 #: src/screens/Messages/Inbox.tsx:187 msgid "Failed to load conversations" msgstr "" @@ -3164,6 +3208,11 @@ msgstr "" msgid "Failed to send" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:122 +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:136 +msgid "Failed to send email, please try again." +msgstr "" + #: src/components/moderation/LabelsOnMeDialog.tsx:254 #: src/screens/Messages/components/ChatDisabled.tsx:87 msgid "Failed to submit appeal, please try again." @@ -3173,6 +3222,15 @@ msgstr "" msgid "Failed to toggle thread mute, please try again" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:149 +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx:83 +msgid "Failed to update email 2FA settings" +msgstr "" + +#: src/components/dialogs/EmailDialog/screens/Update.tsx:195 +msgid "Failed to update email, please try again." +msgstr "" + #: src/components/FeedCard.tsx:285 msgid "Failed to update feeds" msgstr "" @@ -3188,11 +3246,15 @@ msgstr "" msgid "Failed to upload video" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:168 +msgid "Failed to verify email, please try again." +msgstr "" + #: src/screens/Settings/components/ChangeHandleDialog.tsx:360 msgid "Failed to verify handle. Please try again." msgstr "" -#: src/Navigation.tsx:261 +#: src/Navigation.tsx:263 msgid "Feed" msgstr "" @@ -3221,11 +3283,11 @@ msgctxt "toast" msgid "Feedback sent!" msgstr "" -#: src/Navigation.tsx:446 +#: src/Navigation.tsx:448 #: src/screens/Search/SearchResults.tsx:68 #: src/screens/StarterPack/StarterPackScreen.tsx:185 -#: src/view/screens/Feeds.tsx:508 -#: src/view/screens/Profile.tsx:227 +#: src/view/screens/Feeds.tsx:511 +#: src/view/screens/Profile.tsx:230 #: src/view/screens/SavedFeeds.tsx:96 #: src/view/shell/desktop/LeftNav.tsx:655 #: src/view/shell/Drawer.tsx:502 @@ -3318,7 +3380,7 @@ msgstr "" #: src/components/ProfileHoverCard/index.web.tsx:456 #: src/components/ProfileHoverCard/index.web.tsx:467 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:223 -#: src/screens/VideoFeed/index.tsx:831 +#: src/screens/VideoFeed/index.tsx:839 #: src/view/com/post-thread/PostThreadFollowBtn.tsx:131 msgid "Follow" msgstr "" @@ -3333,7 +3395,7 @@ msgstr "" msgid "Follow {0}" msgstr "" -#: src/screens/VideoFeed/index.tsx:808 +#: src/screens/VideoFeed/index.tsx:816 msgid "Follow {handle}" msgstr "" @@ -3386,7 +3448,7 @@ msgstr "" msgid "Followed by <0>{0}, <1>{1}, and {2, plural, one {# other} other {# others}}" msgstr "" -#: src/Navigation.tsx:215 +#: src/Navigation.tsx:217 msgid "Followers of @{0} that you know" msgstr "" @@ -3400,9 +3462,9 @@ msgstr "" #: src/components/ProfileHoverCard/index.web.tsx:455 #: src/components/ProfileHoverCard/index.web.tsx:466 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:219 -#: src/screens/VideoFeed/index.tsx:829 +#: src/screens/VideoFeed/index.tsx:837 #: src/view/com/post-thread/PostThreadFollowBtn.tsx:134 -#: src/view/screens/Feeds.tsx:599 +#: src/view/screens/Feeds.tsx:602 #: src/view/screens/SavedFeeds.tsx:422 msgid "Following" msgstr "" @@ -3412,7 +3474,7 @@ msgstr "" msgid "Following {0}" msgstr "" -#: src/screens/VideoFeed/index.tsx:807 +#: src/screens/VideoFeed/index.tsx:815 msgid "Following {handle}" msgstr "" @@ -3425,7 +3487,7 @@ msgstr "" msgid "Following feed preferences" msgstr "" -#: src/Navigation.tsx:332 +#: src/Navigation.tsx:334 #: src/screens/Settings/FollowingFeedPreferences.tsx:53 msgid "Following Feed Preferences" msgstr "" @@ -3502,7 +3564,7 @@ msgstr "" msgid "Gallery" msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:301 +#: src/components/StarterPack/ProfileStarterPacks.tsx:307 msgid "Generate a starter pack" msgstr "" @@ -3510,6 +3572,8 @@ msgstr "" msgid "Get help" msgstr "" +#: src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx:72 +#: src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx:82 #: src/components/dialogs/VerifyEmailDialog.tsx:263 #: src/components/dialogs/VerifyEmailDialog.tsx:269 msgid "Get started" @@ -3536,10 +3600,10 @@ msgstr "" #: src/components/moderation/ScreenHider.tsx:154 #: src/components/moderation/ScreenHider.tsx:163 #: src/screens/Messages/Inbox.tsx:228 -#: src/screens/Profile/ProfileFeed/index.tsx:89 +#: src/screens/Profile/ProfileFeed/index.tsx:92 #: src/screens/VideoFeed/components/Header.tsx:163 -#: src/screens/VideoFeed/index.tsx:1110 -#: src/screens/VideoFeed/index.tsx:1114 +#: src/screens/VideoFeed/index.tsx:1118 +#: src/screens/VideoFeed/index.tsx:1122 #: src/view/com/auth/LoggedOut.tsx:72 #: src/view/screens/NotFound.tsx:57 #: src/view/screens/ProfileList.tsx:1038 @@ -3550,7 +3614,7 @@ msgstr "" #: src/screens/List/ListHiddenScreen.tsx:224 #: src/screens/Profile/ErrorState.tsx:62 #: src/screens/Profile/ErrorState.tsx:66 -#: src/screens/Profile/ProfileFeed/index.tsx:94 +#: src/screens/Profile/ProfileFeed/index.tsx:97 #: src/screens/StarterPack/StarterPackScreen.tsx:757 #: src/view/screens/NotFound.tsx:56 #: src/view/screens/ProfileList.tsx:1043 @@ -3607,8 +3671,8 @@ msgstr "" msgid "Half way there!" msgstr "" -#: src/screens/Settings/AccountSettings.tsx:128 -#: src/screens/Settings/AccountSettings.tsx:133 +#: src/screens/Settings/AccountSettings.tsx:137 +#: src/screens/Settings/AccountSettings.tsx:142 msgid "Handle" msgstr "" @@ -3634,7 +3698,7 @@ msgstr "" msgid "Harassment, trolling, or intolerance" msgstr "" -#: src/Navigation.tsx:416 +#: src/Navigation.tsx:418 msgid "Hashtag" msgstr "" @@ -3642,6 +3706,11 @@ msgstr "" msgid "Hashtag {tag}" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:198 +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:315 +msgid "Have a code? <0>Click here." +msgstr "" + #: src/screens/Signup/index.tsx:195 msgid "Having trouble?" msgstr "" @@ -3667,7 +3736,7 @@ msgstr "" msgid "Hidden" msgstr "" -#: src/screens/VideoFeed/index.tsx:609 +#: src/screens/VideoFeed/index.tsx:617 msgid "Hidden by your moderation settings." msgstr "" @@ -3786,8 +3855,8 @@ msgstr "" msgid "Hold up! We’re gradually giving access to video, and you’re still waiting in line. Check back soon!" msgstr "" -#: src/Navigation.tsx:645 -#: src/Navigation.tsx:665 +#: src/Navigation.tsx:647 +#: src/Navigation.tsx:667 #: src/view/shell/bottom-bar/BottomBar.tsx:166 #: src/view/shell/desktop/LeftNav.tsx:599 #: src/view/shell/Drawer.tsx:412 @@ -3854,10 +3923,18 @@ msgstr "" msgid "If you have your own domain, you can use that as your handle. This lets you self-verify your identity. <0>Learn more here." msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:270 +msgid "If you need to update your email, <0>click here." +msgstr "" + #: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:796 msgid "If you remove this post, you won't be able to recover it." msgstr "" +#: src/components/dialogs/EmailDialog/screens/Update.tsx:208 +msgid "If you update your email address, email 2FA will be disabled." +msgstr "" + #: src/view/com/modals/ChangePassword.tsx:160 msgid "If you want to change your password, we will send you a code to verify that this is your account." msgstr "" @@ -4005,7 +4082,7 @@ msgstr "" msgid "It's just you right now! Add more people to your starter pack by searching above." msgstr "" -#: src/view/com/composer/Composer.tsx:1633 +#: src/view/com/composer/Composer.tsx:1612 msgid "Job ID: {0}" msgstr "" @@ -4040,7 +4117,7 @@ msgid "Labeled by the author." msgstr "" #: src/view/com/composer/labels/LabelsBtn.tsx:75 -#: src/view/screens/Profile.tsx:220 +#: src/view/screens/Profile.tsx:223 msgid "Labels" msgstr "" @@ -4064,7 +4141,7 @@ msgstr "" msgid "Language selection" msgstr "" -#: src/Navigation.tsx:188 +#: src/Navigation.tsx:190 msgid "Language Settings" msgstr "" @@ -4166,7 +4243,7 @@ msgstr "" msgid "left to go." msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:317 +#: src/components/StarterPack/ProfileStarterPacks.tsx:323 msgid "Let me choose" msgstr "" @@ -4215,8 +4292,8 @@ msgstr "" msgid "Like this labeler" msgstr "" -#: src/Navigation.tsx:266 -#: src/Navigation.tsx:271 +#: src/Navigation.tsx:268 +#: src/Navigation.tsx:273 msgid "Liked by" msgstr "" @@ -4238,7 +4315,7 @@ msgstr "" msgid "Liked by {likeCount, plural, one {# user} other {# users}}" msgstr "" -#: src/view/screens/Profile.tsx:226 +#: src/view/screens/Profile.tsx:229 msgid "Likes" msgstr "" @@ -4251,7 +4328,7 @@ msgstr "" msgid "Linear" msgstr "" -#: src/Navigation.tsx:221 +#: src/Navigation.tsx:223 msgid "List" msgstr "" @@ -4309,10 +4386,10 @@ msgctxt "toast" msgid "List unmuted" msgstr "" -#: src/Navigation.tsx:142 -#: src/view/screens/Lists.tsx:62 -#: src/view/screens/Profile.tsx:221 -#: src/view/screens/Profile.tsx:229 +#: src/Navigation.tsx:144 +#: src/view/screens/Lists.tsx:65 +#: src/view/screens/Profile.tsx:224 +#: src/view/screens/Profile.tsx:232 #: src/view/shell/desktop/LeftNav.tsx:673 #: src/view/shell/Drawer.tsx:517 msgid "Lists" @@ -4334,7 +4411,7 @@ msgstr "" msgid "Load new notifications" msgstr "" -#: src/screens/Profile/ProfileFeed/index.tsx:221 +#: src/screens/Profile/ProfileFeed/index.tsx:224 #: src/screens/Profile/Sections/Feed.tsx:98 #: src/view/com/feeds/FeedPage.tsx:155 #: src/view/screens/ProfileList.tsx:878 @@ -4345,7 +4422,7 @@ msgstr "" msgid "Loading..." msgstr "" -#: src/Navigation.tsx:291 +#: src/Navigation.tsx:293 msgid "Log" msgstr "" @@ -4382,7 +4459,11 @@ msgstr "" msgid "Looks like you're missing a following feed. <0>Click here to add one." msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:270 +#: src/components/dialogs/EmailDialog/index.tsx:40 +msgid "Make adjustments to email settings for your account" +msgstr "" + +#: src/components/StarterPack/ProfileStarterPacks.tsx:282 msgid "Make one for me" msgstr "" @@ -4419,12 +4500,14 @@ msgstr "" msgid "Marked all as read" msgstr "" +#: src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx:86 +#: src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx:93 #: src/components/dialogs/VerifyEmailDialog.tsx:273 #: src/components/dialogs/VerifyEmailDialog.tsx:281 msgid "Maybe later" msgstr "" -#: src/view/screens/Profile.tsx:224 +#: src/view/screens/Profile.tsx:227 msgid "Media" msgstr "" @@ -4448,7 +4531,7 @@ msgstr "" msgid "Menu" msgstr "" -#: src/components/dms/MessageProfileButton.tsx:103 +#: src/components/dms/MessageProfileButton.tsx:96 msgid "Message {0}" msgstr "" @@ -4482,7 +4565,7 @@ msgstr "" msgid "Message options" msgstr "" -#: src/Navigation.tsx:660 +#: src/Navigation.tsx:662 msgid "Messages" msgstr "" @@ -4501,7 +4584,7 @@ msgstr "" msgid "Misleading Post" msgstr "" -#: src/Navigation.tsx:147 +#: src/Navigation.tsx:149 #: src/screens/Moderation/index.tsx:93 #: src/screens/Settings/Settings.tsx:176 #: src/screens/Settings/Settings.tsx:179 @@ -4540,8 +4623,8 @@ msgstr "" msgid "Moderation lists" msgstr "" -#: src/Navigation.tsx:152 -#: src/view/screens/ModerationModlists.tsx:62 +#: src/Navigation.tsx:154 +#: src/view/screens/ModerationModlists.tsx:65 msgid "Moderation Lists" msgstr "" @@ -4549,7 +4632,7 @@ msgstr "" msgid "moderation settings" msgstr "" -#: src/Navigation.tsx:281 +#: src/Navigation.tsx:283 msgid "Moderation states" msgstr "" @@ -4672,7 +4755,7 @@ msgstr "" msgid "Muted accounts" msgstr "" -#: src/Navigation.tsx:157 +#: src/Navigation.tsx:159 #: src/view/screens/ModerationMutedAccounts.tsx:118 msgid "Muted Accounts" msgstr "" @@ -4698,7 +4781,7 @@ msgstr "" msgid "My Birthday" msgstr "" -#: src/view/screens/Feeds.tsx:699 +#: src/view/screens/Feeds.tsx:702 msgid "My Feeds" msgstr "" @@ -4766,19 +4849,20 @@ msgstr "" msgid "New" msgstr "" -#: src/view/screens/Lists.tsx:74 -#: src/view/screens/ModerationModlists.tsx:74 +#: src/view/screens/Lists.tsx:77 +#: src/view/screens/ModerationModlists.tsx:77 msgctxt "action" msgid "New" msgstr "" -#: src/components/dms/dialogs/NewChatDialog.tsx:65 -#: src/screens/Messages/ChatList.tsx:351 -#: src/screens/Messages/ChatList.tsx:358 +#: src/components/dms/dialogs/NewChatDialog.tsx:67 +#: src/screens/Messages/ChatList.tsx:364 +#: src/screens/Messages/ChatList.tsx:371 msgid "New chat" msgstr "" #: src/components/dialogs/ChangeEmailDialog.tsx:142 +#: src/components/dialogs/EmailDialog/screens/Update.tsx:222 msgid "New email address" msgstr "" @@ -4792,8 +4876,8 @@ msgstr "" msgid "New handle" msgstr "" -#: src/view/screens/Lists.tsx:66 -#: src/view/screens/ModerationModlists.tsx:66 +#: src/view/screens/Lists.tsx:69 +#: src/view/screens/ModerationModlists.tsx:69 msgid "New list" msgstr "" @@ -4813,10 +4897,10 @@ msgstr "" msgid "New Password" msgstr "" -#: src/screens/Profile/ProfileFeed/index.tsx:238 -#: src/view/screens/Feeds.tsx:549 +#: src/screens/Profile/ProfileFeed/index.tsx:241 +#: src/view/screens/Feeds.tsx:552 #: src/view/screens/Notifications.tsx:165 -#: src/view/screens/Profile.tsx:507 +#: src/view/screens/Profile.tsx:510 #: src/view/screens/ProfileList.tsx:250 #: src/view/screens/ProfileList.tsx:288 msgid "New post" @@ -4953,7 +5037,7 @@ msgstr "" msgid "No results found" msgstr "" -#: src/view/screens/Feeds.tsx:470 +#: src/view/screens/Feeds.tsx:473 msgid "No results found for \"{query}\"" msgstr "" @@ -5005,8 +5089,8 @@ msgstr "" msgid "Not followed by anyone you're following" msgstr "" -#: src/Navigation.tsx:137 -#: src/view/screens/Profile.tsx:122 +#: src/Navigation.tsx:139 +#: src/view/screens/Profile.tsx:125 msgid "Not Found" msgstr "" @@ -5020,7 +5104,7 @@ msgstr "" msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites." msgstr "" -#: src/screens/Messages/ChatList.tsx:264 +#: src/screens/Messages/ChatList.tsx:265 msgid "Nothing here" msgstr "" @@ -5028,7 +5112,7 @@ msgstr "" msgid "Notification filters" msgstr "" -#: src/Navigation.tsx:441 +#: src/Navigation.tsx:443 #: src/view/screens/Notifications.tsx:134 msgid "Notification settings" msgstr "" @@ -5045,7 +5129,7 @@ msgstr "" msgid "Notification Sounds" msgstr "" -#: src/Navigation.tsx:655 +#: src/Navigation.tsx:657 #: src/view/screens/Notifications.tsx:128 #: src/view/shell/bottom-bar/BottomBar.tsx:240 #: src/view/shell/desktop/LeftNav.tsx:636 @@ -5112,15 +5196,15 @@ msgstr "" msgid "Onboarding reset" msgstr "" -#: src/view/com/composer/Composer.tsx:354 +#: src/view/com/composer/Composer.tsx:342 msgid "One or more GIFs is missing alt text." msgstr "" -#: src/view/com/composer/Composer.tsx:351 +#: src/view/com/composer/Composer.tsx:339 msgid "One or more images is missing alt text." msgstr "" -#: src/view/com/composer/Composer.tsx:361 +#: src/view/com/composer/Composer.tsx:349 msgid "One or more videos is missing alt text." msgstr "" @@ -5149,12 +5233,12 @@ msgid "Oops, something went wrong!" msgstr "" #: src/components/Lists.tsx:173 -#: src/components/StarterPack/ProfileStarterPacks.tsx:326 -#: src/components/StarterPack/ProfileStarterPacks.tsx:335 +#: src/components/StarterPack/ProfileStarterPacks.tsx:332 +#: src/components/StarterPack/ProfileStarterPacks.tsx:341 #: src/screens/Settings/AppPasswords.tsx:59 #: src/screens/Settings/components/ChangeHandleDialog.tsx:106 #: src/screens/Settings/NotificationSettings.tsx:54 -#: src/view/screens/Profile.tsx:122 +#: src/view/screens/Profile.tsx:125 msgid "Oops!" msgstr "" @@ -5180,7 +5264,7 @@ msgid "Open drawer menu" msgstr "" #: src/screens/Messages/components/MessageInput.web.tsx:181 -#: src/view/com/composer/Composer.tsx:1293 +#: src/view/com/composer/Composer.tsx:1272 msgid "Open emoji picker" msgstr "" @@ -5262,7 +5346,7 @@ msgstr "" msgid "Opens captions and alt text dialog" msgstr "" -#: src/screens/Settings/AccountSettings.tsx:129 +#: src/screens/Settings/AccountSettings.tsx:138 msgid "Opens change handle dialog" msgstr "" @@ -5274,7 +5358,7 @@ msgstr "" msgid "Opens device photo gallery" msgstr "" -#: src/view/com/composer/Composer.tsx:1294 +#: src/view/com/composer/Composer.tsx:1273 msgid "Opens emoji picker" msgstr "" @@ -5313,7 +5397,7 @@ msgstr "" msgid "Opens this profile" msgstr "" -#: src/view/com/composer/videos/SelectVideoBtn.tsx:103 +#: src/view/com/composer/videos/SelectVideoBtn.tsx:75 msgid "Opens video picker" msgstr "" @@ -5386,8 +5470,8 @@ msgid "Page Not Found" msgstr "" #: src/screens/Login/LoginForm.tsx:228 -#: src/screens/Settings/AccountSettings.tsx:119 -#: src/screens/Settings/AccountSettings.tsx:123 +#: src/screens/Settings/AccountSettings.tsx:128 +#: src/screens/Settings/AccountSettings.tsx:132 #: src/screens/Signup/StepInfo/index.tsx:228 #: src/view/com/modals/DeleteAccount.tsx:239 #: src/view/com/modals/DeleteAccount.tsx:246 @@ -5426,11 +5510,11 @@ msgstr "" msgid "People" msgstr "" -#: src/Navigation.tsx:208 +#: src/Navigation.tsx:210 msgid "People followed by @{0}" msgstr "" -#: src/Navigation.tsx:201 +#: src/Navigation.tsx:203 msgid "People following @{0}" msgstr "" @@ -5546,6 +5630,10 @@ msgstr "" msgid "Please choose your password." msgstr "" +#: src/components/dialogs/EmailDialog/screens/Update.tsx:290 +msgid "Please click on the link in the email we just sent you to verify your new email address. This is an important step to allow you to continue enjoying all the features of Bluesky." +msgstr "" + #: src/screens/Signup/state.ts:301 msgid "Please complete the verification captcha." msgstr "" @@ -5566,10 +5654,28 @@ msgstr "" msgid "Please enter a unique name for this app password or use our randomly generated one." msgstr "" +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:131 +#: src/components/dialogs/EmailDialog/screens/Update.tsx:135 +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:145 +msgid "Please enter a valid code." +msgstr "" + +#: src/components/dialogs/EmailDialog/screens/Update.tsx:148 +msgid "Please enter a valid email address." +msgstr "" + #: src/components/dialogs/MutedWords.tsx:86 msgid "Please enter a valid word, tag, or phrase to mute" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:257 +msgid "Please enter the code we sent to <0>{0} below." +msgstr "" + +#: src/components/dialogs/EmailDialog/screens/Update.tsx:247 +msgid "Please enter the security code we sent to your previous email address." +msgstr "" + #: src/screens/Signup/state.ts:262 #: src/screens/Signup/StepInfo/index.tsx:91 msgid "Please enter your email." @@ -5579,6 +5685,10 @@ msgstr "" msgid "Please enter your invite code." msgstr "" +#: src/components/dialogs/EmailDialog/screens/Update.tsx:217 +msgid "Please enter your new email address." +msgstr "" + #: src/screens/Login/LoginForm.tsx:99 msgid "Please enter your password" msgstr "" @@ -5612,6 +5722,10 @@ msgstr "" msgid "Please sign in as @{0}" msgstr "" +#: src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx:58 +msgid "Please verify your email" +msgstr "" + #: src/components/dialogs/VerifyEmailDialog.tsx:108 msgid "Please Verify Your Email" msgstr "" @@ -5626,7 +5740,7 @@ msgstr "" msgid "Porn" msgstr "" -#: src/view/com/composer/Composer.tsx:1008 +#: src/view/com/composer/Composer.tsx:987 msgctxt "action" msgid "Post" msgstr "" @@ -5636,7 +5750,7 @@ msgctxt "description" msgid "Post" msgstr "" -#: src/view/com/composer/Composer.tsx:1006 +#: src/view/com/composer/Composer.tsx:985 msgctxt "action" msgid "Post All" msgstr "" @@ -5645,10 +5759,10 @@ msgstr "" msgid "Post by {0}" msgstr "" -#: src/Navigation.tsx:234 -#: src/Navigation.tsx:241 -#: src/Navigation.tsx:248 -#: src/Navigation.tsx:255 +#: src/Navigation.tsx:236 +#: src/Navigation.tsx:243 +#: src/Navigation.tsx:250 +#: src/Navigation.tsx:257 msgid "Post by @{0}" msgstr "" @@ -5661,7 +5775,7 @@ msgstr "" msgid "Post failed to upload. Please check your Internet connection and try again." msgstr "" -#: src/screens/VideoFeed/index.tsx:514 +#: src/screens/VideoFeed/index.tsx:522 msgid "Post has been deleted" msgstr "" @@ -5683,7 +5797,7 @@ msgstr "" msgid "Post interaction settings" msgstr "" -#: src/Navigation.tsx:168 +#: src/Navigation.tsx:170 #: src/screens/ModerationInteractionSettings/index.tsx:34 msgid "Post Interaction Settings" msgstr "" @@ -5712,7 +5826,7 @@ msgid "Post unpinned" msgstr "" #: src/screens/StarterPack/StarterPackScreen.tsx:186 -#: src/view/screens/Profile.tsx:222 +#: src/view/screens/Profile.tsx:225 #: src/view/screens/ProfileList.tsx:170 msgid "Posts" msgstr "" @@ -5776,12 +5890,12 @@ msgstr "" msgid "Privacy and security" msgstr "" -#: src/Navigation.tsx:377 +#: src/Navigation.tsx:379 #: src/screens/Settings/PrivacyAndSecuritySettings.tsx:36 msgid "Privacy and Security" msgstr "" -#: src/Navigation.tsx:301 +#: src/Navigation.tsx:303 #: src/screens/Settings/AboutSettings.tsx:92 #: src/screens/Settings/AboutSettings.tsx:95 #: src/view/screens/PrivacyPolicy.tsx:31 @@ -5790,7 +5904,7 @@ msgstr "" msgid "Privacy Policy" msgstr "" -#: src/view/com/composer/Composer.tsx:1696 +#: src/view/com/composer/Composer.tsx:1675 msgid "Processing video..." msgstr "" @@ -5800,7 +5914,7 @@ msgid "Processing..." msgstr "" #: src/view/screens/DebugMod.tsx:926 -#: src/view/screens/Profile.tsx:361 +#: src/view/screens/Profile.tsx:364 msgid "profile" msgstr "" @@ -5830,22 +5944,22 @@ msgid "Public, sharable lists which can be used to drive feeds." msgstr "" #. Accessibility label for button to publish a single post -#: src/view/com/composer/Composer.tsx:988 +#: src/view/com/composer/Composer.tsx:967 msgid "Publish post" msgstr "" #. Accessibility label for button to publish multiple posts in a thread -#: src/view/com/composer/Composer.tsx:981 +#: src/view/com/composer/Composer.tsx:960 msgid "Publish posts" msgstr "" #. Accessibility label for button to publish multiple replies in a thread -#: src/view/com/composer/Composer.tsx:966 +#: src/view/com/composer/Composer.tsx:945 msgid "Publish replies" msgstr "" #. Accessibility label for button to publish a single reply -#: src/view/com/composer/Composer.tsx:973 +#: src/view/com/composer/Composer.tsx:952 msgid "Publish reply" msgstr "" @@ -5924,11 +6038,11 @@ msgstr "" msgid "Read blog post" msgstr "" -#: src/screens/VideoFeed/index.tsx:944 +#: src/screens/VideoFeed/index.tsx:952 msgid "Read less" msgstr "" -#: src/screens/VideoFeed/index.tsx:944 +#: src/screens/VideoFeed/index.tsx:952 msgid "Read more" msgstr "" @@ -5968,15 +6082,15 @@ msgid "Reconnect" msgstr "" #. Reject a chat request, this opens a menu with options -#: src/screens/Messages/components/RequestButtons.tsx:114 +#: src/screens/Messages/components/RequestButtons.tsx:124 msgid "Reject" msgstr "" -#: src/screens/Messages/components/RequestButtons.tsx:102 +#: src/screens/Messages/components/RequestButtons.tsx:112 msgid "Reject chat request" msgstr "" -#: src/screens/Messages/ChatList.tsx:247 +#: src/screens/Messages/ChatList.tsx:248 #: src/screens/Messages/Inbox.tsx:191 msgid "Reload conversations" msgstr "" @@ -6141,7 +6255,7 @@ msgstr "" msgid "Replace with Discover" msgstr "" -#: src/view/screens/Profile.tsx:223 +#: src/view/screens/Profile.tsx:226 msgid "Replies" msgstr "" @@ -6153,7 +6267,7 @@ msgstr "" msgid "Replies to this post are disabled." msgstr "" -#: src/view/com/composer/Composer.tsx:1004 +#: src/view/com/composer/Composer.tsx:983 msgctxt "action" msgid "Reply" msgstr "" @@ -6184,7 +6298,7 @@ msgstr "" msgid "Reply sorting" msgstr "" -#: src/view/com/post/Post.tsx:204 +#: src/view/com/post/Post.tsx:208 #: src/view/com/posts/PostFeedItem.tsx:570 msgctxt "description" msgid "Reply to <0><1/>" @@ -6200,7 +6314,7 @@ msgctxt "description" msgid "Reply to a post" msgstr "" -#: src/view/com/post/Post.tsx:202 +#: src/view/com/post/Post.tsx:206 #: src/view/com/posts/PostFeedItem.tsx:567 msgctxt "description" msgid "Reply to you" @@ -6229,8 +6343,8 @@ msgstr "" #: src/components/dms/ConvoMenu.tsx:255 #: src/components/dms/ConvoMenu.tsx:258 #: src/components/dms/ReportConversationPrompt.tsx:17 -#: src/screens/Messages/components/RequestButtons.tsx:145 -#: src/screens/Messages/components/RequestButtons.tsx:148 +#: src/screens/Messages/components/RequestButtons.tsx:155 +#: src/screens/Messages/components/RequestButtons.tsx:158 msgid "Report conversation" msgstr "" @@ -6366,7 +6480,7 @@ msgstr "" msgid "Require alt text before posting" msgstr "" -#: src/screens/Settings/components/Email2FAToggle.tsx:58 +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx:96 msgid "Require an email code to sign in to your account." msgstr "" @@ -6378,6 +6492,10 @@ msgstr "" msgid "Required in your region" msgstr "" +#: src/components/dialogs/EmailDialog/components/ResendEmailText.tsx:40 +msgid "Resend" +msgstr "" + #: src/components/dialogs/ChangeEmailDialog.tsx:217 #: src/components/dialogs/ChangeEmailDialog.tsx:227 #: src/components/dialogs/VerifyEmailDialog.tsx:330 @@ -6425,10 +6543,10 @@ msgstr "" #: src/components/Error.tsx:65 #: src/components/Lists.tsx:110 #: src/components/moderation/ReportDialog/index.tsx:221 -#: src/components/StarterPack/ProfileStarterPacks.tsx:340 +#: src/components/StarterPack/ProfileStarterPacks.tsx:346 #: src/screens/Login/LoginForm.tsx:323 #: src/screens/Login/LoginForm.tsx:330 -#: src/screens/Messages/ChatList.tsx:253 +#: src/screens/Messages/ChatList.tsx:254 #: src/screens/Messages/components/MessageListError.tsx:25 #: src/screens/Messages/Inbox.tsx:197 #: src/screens/Onboarding/StepInterests/index.tsx:217 @@ -6455,9 +6573,9 @@ msgstr "" msgid "Returns to home page" msgstr "" -#: src/screens/Profile/ProfileFeed/index.tsx:90 +#: src/screens/Profile/ProfileFeed/index.tsx:93 #: src/screens/Settings/components/ChangeHandleDialog.tsx:559 -#: src/screens/VideoFeed/index.tsx:1111 +#: src/screens/VideoFeed/index.tsx:1119 #: src/view/screens/NotFound.tsx:60 #: src/view/screens/ProfileList.tsx:1039 msgid "Returns to previous page" @@ -6573,7 +6691,7 @@ msgstr "" msgid "Search" msgstr "" -#: src/Navigation.tsx:227 +#: src/Navigation.tsx:229 #: src/screens/Profile/ProfileSearch.tsx:37 msgid "Search @{0}'s posts" msgstr "" @@ -6582,7 +6700,7 @@ msgstr "" msgid "Search by name or interest" msgstr "" -#: src/view/screens/Feeds.tsx:441 +#: src/view/screens/Feeds.tsx:444 msgid "Search feeds" msgstr "" @@ -6646,6 +6764,10 @@ msgstr "" msgid "Searches for profiles" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Update.tsx:243 +msgid "Security step required" +msgstr "" + #: src/components/dialogs/ChangeEmailDialog.tsx:57 msgid "Security Step Required" msgstr "" @@ -6759,7 +6881,7 @@ msgstr "" msgid "Select the moderation service(s) to report to" msgstr "" -#: src/view/com/composer/videos/SelectVideoBtn.tsx:102 +#: src/view/com/composer/videos/SelectVideoBtn.tsx:74 msgid "Select video" msgstr "" @@ -6804,6 +6926,9 @@ msgstr "" msgid "Send confirmation email" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:174 +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:181 +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:300 #: src/view/com/modals/DeleteAccount.tsx:145 msgid "Send email" msgstr "" @@ -6841,6 +6966,7 @@ msgstr "" msgid "Send report to {title}" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:293 #: src/screens/Settings/components/DisableEmail2FADialog.tsx:120 #: src/screens/Settings/components/DisableEmail2FADialog.tsx:123 msgid "Send verification email" @@ -6879,7 +7005,7 @@ msgstr "" msgid "Sets email for password reset" msgstr "" -#: src/Navigation.tsx:183 +#: src/Navigation.tsx:185 #: src/screens/Settings/Settings.tsx:89 #: src/view/shell/desktop/LeftNav.tsx:709 #: src/view/shell/Drawer.tsx:555 @@ -6968,7 +7094,7 @@ msgstr "" msgid "Share your favorite feed!" msgstr "" -#: src/Navigation.tsx:286 +#: src/Navigation.tsx:288 msgid "Shared Preferences Tester" msgstr "" @@ -6989,8 +7115,8 @@ msgstr "" #: src/components/moderation/ScreenHider.tsx:172 #: src/components/moderation/ScreenHider.tsx:175 #: src/screens/List/ListHiddenScreen.tsx:190 -#: src/screens/VideoFeed/index.tsx:612 -#: src/screens/VideoFeed/index.tsx:618 +#: src/screens/VideoFeed/index.tsx:620 +#: src/screens/VideoFeed/index.tsx:626 msgid "Show anyway" msgstr "" @@ -7021,7 +7147,7 @@ msgid "Show list anyway" msgstr "" #: src/view/com/post-thread/PostThreadItem.tsx:625 -#: src/view/com/post/Post.tsx:243 +#: src/view/com/post/Post.tsx:247 #: src/view/com/posts/PostFeedItem.tsx:526 msgid "Show More" msgstr "" @@ -7184,6 +7310,7 @@ msgstr "" msgid "Smaller" msgstr "" +#: src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx:87 #: src/components/dialogs/VerifyEmailDialog.tsx:274 msgid "Snoozes the reminder" msgstr "" @@ -7217,7 +7344,7 @@ msgstr "" msgid "Something wasn't quite right with the data you're trying to report. Please contact support." msgstr "" -#: src/screens/Messages/Conversation.tsx:126 +#: src/screens/Messages/Conversation.tsx:129 msgid "Something went wrong" msgstr "" @@ -7286,7 +7413,7 @@ msgstr "" msgid "Sports" msgstr "" -#: src/components/dms/dialogs/NewChatDialog.tsx:72 +#: src/components/dms/dialogs/NewChatDialog.tsx:74 msgid "Start a new chat" msgstr "" @@ -7304,8 +7431,8 @@ msgstr "" msgid "Start chat with {displayName}" msgstr "" -#: src/Navigation.tsx:451 -#: src/Navigation.tsx:456 +#: src/Navigation.tsx:453 +#: src/Navigation.tsx:458 #: src/screens/StarterPack/Wizard/index.tsx:186 msgid "Starter Pack" msgstr "" @@ -7328,11 +7455,11 @@ msgid "Starter pack is invalid" msgstr "" #: src/screens/Search/Explore.tsx:538 -#: src/view/screens/Profile.tsx:228 +#: src/view/screens/Profile.tsx:231 msgid "Starter Packs" msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:248 +#: src/components/StarterPack/ProfileStarterPacks.tsx:266 msgid "Starter packs let you easily share your favorite feeds and people with your friends." msgstr "" @@ -7349,7 +7476,7 @@ msgstr "" msgid "Storage cleared, you need to restart the app now." msgstr "" -#: src/Navigation.tsx:276 +#: src/Navigation.tsx:278 #: src/screens/Settings/Settings.tsx:387 msgid "Storybook" msgstr "" @@ -7395,6 +7522,7 @@ msgstr "" msgid "Subscribe to this list" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Update.tsx:286 #: src/components/dialogs/VerifyEmailDialog.tsx:124 msgid "Success!" msgstr "" @@ -7426,7 +7554,7 @@ msgctxt "Name of app icon variant" msgid "Sunset" msgstr "" -#: src/Navigation.tsx:296 +#: src/Navigation.tsx:298 #: src/view/screens/Support.tsx:31 #: src/view/screens/Support.tsx:34 msgid "Support" @@ -7513,7 +7641,7 @@ msgstr "" msgid "Terms" msgstr "" -#: src/Navigation.tsx:306 +#: src/Navigation.tsx:308 #: src/screens/Settings/AboutSettings.tsx:84 #: src/screens/Settings/AboutSettings.tsx:87 #: src/view/screens/TermsOfService.tsx:31 @@ -7583,7 +7711,7 @@ msgstr "" msgid "That's all, folks!" msgstr "" -#: src/screens/VideoFeed/index.tsx:1083 +#: src/screens/VideoFeed/index.tsx:1091 msgid "That's everything!" msgstr "" @@ -7659,6 +7787,7 @@ msgstr "" msgid "The selected video is larger than 100 MB." msgstr "" +#: src/lib/hooks/useCleanError.ts:40 #: src/lib/strings/errors.ts:18 msgid "The server appears to be experiencing issues. Please try again in a few moments." msgstr "" @@ -7855,10 +7984,18 @@ msgstr "" msgid "This conversation is with a deleted or a deactivated account. Press for options" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Update.tsx:156 +msgid "This email is already associated with your account." +msgstr "" + #: src/screens/Settings/components/ExportCarDialog.tsx:96 msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost." msgstr "" +#: src/lib/hooks/useCleanError.ts:49 +msgid "This feature is not available while using an app password. Please sign in with your main password." +msgstr "" + #: src/lib/strings/errors.ts:21 msgid "This feature is not available while using an App Password. Please sign in with your main password." msgstr "" @@ -7872,7 +8009,7 @@ msgid "This feed is empty! You may need to follow more users or tune your langua msgstr "" #: src/components/StarterPack/Main/PostsList.tsx:36 -#: src/screens/Profile/ProfileFeed/index.tsx:189 +#: src/screens/Profile/ProfileFeed/index.tsx:192 #: src/view/screens/ProfileList.tsx:843 msgid "This feed is empty." msgstr "" @@ -7942,7 +8079,7 @@ msgstr "" msgid "This post will be hidden from feeds and threads. This cannot be undone." msgstr "" -#: src/view/com/composer/Composer.tsx:436 +#: src/view/com/composer/Composer.tsx:424 msgid "This post's author has disabled quote posts." msgstr "" @@ -8034,7 +8171,7 @@ msgstr "" msgid "Threaded mode" msgstr "" -#: src/Navigation.tsx:339 +#: src/Navigation.tsx:341 msgid "Threads Preferences" msgstr "" @@ -8046,12 +8183,13 @@ msgstr "" msgid "To disable the email 2FA method, please verify your access to the email address." msgstr "" -#: src/components/dms/ReportConversationPrompt.tsx:19 -msgid "To report a conversation, please report one of its messages via the conversation screen. This lets our moderators understand the context of your issue." +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:164 +#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:215 +msgid "To disable your email 2FA method, please verify your access to <0>{0}" msgstr "" -#: src/view/com/composer/videos/SelectVideoBtn.tsx:129 -msgid "To upload videos to Bluesky, you must first verify your email." +#: src/components/dms/ReportConversationPrompt.tsx:19 +msgid "To report a conversation, please report one of its messages via the conversation screen. This lets our moderators understand the context of your issue." msgstr "" #: src/components/ReportDialog/SelectLabelerView.tsx:31 @@ -8080,7 +8218,7 @@ msgstr "" msgid "Top" msgstr "" -#: src/Navigation.tsx:421 +#: src/Navigation.tsx:423 msgid "Topic" msgstr "" @@ -8134,6 +8272,7 @@ msgstr "" msgid "Type:" msgstr "" +#: src/lib/hooks/useCleanError.ts:27 #: src/lib/strings/errors.ts:11 msgid "Unable to connect. Please check your internet connection and try again." msgstr "" @@ -8207,7 +8346,7 @@ msgstr "" msgid "Unfollow account" msgstr "" -#: src/screens/VideoFeed/index.tsx:812 +#: src/screens/VideoFeed/index.tsx:820 msgid "Unfollows the user" msgstr "" @@ -8323,11 +8462,11 @@ msgstr "" msgid "Unsubscribed from list" msgstr "" -#: src/view/com/composer/Composer.tsx:790 +#: src/view/com/composer/Composer.tsx:769 msgid "Unsupported video type" msgstr "" -#: src/view/com/composer/videos/SelectVideoBtn.tsx:68 +#: src/view/com/composer/videos/SelectVideoBtn.tsx:48 msgid "Unsupported video type: {0}" msgstr "" @@ -8347,11 +8486,22 @@ msgstr "" msgid "Update <0>{displayName} in Lists" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Update.tsx:300 +#: src/components/dialogs/EmailDialog/screens/Update.tsx:312 +#: src/screens/Settings/AccountSettings.tsx:104 +#: src/screens/Settings/AccountSettings.tsx:112 +msgid "Update email" +msgstr "" + #: src/screens/Settings/components/ChangeHandleDialog.tsx:526 #: src/screens/Settings/components/ChangeHandleDialog.tsx:547 msgid "Update to {domain}" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Update.tsx:203 +msgid "Update your email" +msgstr "" + #: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:347 msgctxt "toast" msgid "Updating quote attachment failed" @@ -8402,7 +8552,7 @@ msgstr "" msgid "Uploading link thumbnail..." msgstr "" -#: src/view/com/composer/Composer.tsx:1693 +#: src/view/com/composer/Composer.tsx:1672 msgid "Uploading video..." msgstr "" @@ -8526,7 +8676,7 @@ msgstr "" msgid "Verification settings" msgstr "" -#: src/Navigation.tsx:176 +#: src/Navigation.tsx:178 #: src/screens/Moderation/VerificationSettings.tsx:32 msgid "Verification Settings" msgstr "" @@ -8539,10 +8689,6 @@ msgstr "" msgid "Verified by:" msgstr "" -#: src/view/com/composer/videos/SelectVideoBtn.tsx:127 -msgid "Verified email required" -msgstr "" - #: src/components/verification/VerificationCreatePrompt.tsx:84 #: src/components/verification/VerificationCreatePrompt.tsx:86 #: src/view/com/profile/ProfileMenu.tsx:308 @@ -8550,6 +8696,11 @@ msgstr "" msgid "Verify account" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:348 +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:359 +msgid "Verify code" +msgstr "" + #: src/screens/Settings/components/ChangeHandleDialog.tsx:528 #: src/screens/Settings/components/ChangeHandleDialog.tsx:549 msgid "Verify DNS Record" @@ -8560,16 +8711,16 @@ msgstr "" msgid "Verify email" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:214 +msgid "Verify email code" +msgstr "" + #: src/components/dialogs/ChangeEmailDialog.tsx:122 #: src/components/dialogs/VerifyEmailDialog.tsx:163 #: src/components/intents/VerifyEmailIntentDialog.tsx:67 msgid "Verify email dialog" msgstr "" -#: src/view/com/composer/videos/SelectVideoBtn.tsx:131 -msgid "Verify now" -msgstr "" - #: src/screens/Settings/components/ChangeHandleDialog.tsx:529 #: src/screens/Settings/components/ChangeHandleDialog.tsx:551 msgid "Verify Text File" @@ -8579,8 +8730,9 @@ msgstr "" msgid "Verify this account?" msgstr "" -#: src/screens/Settings/AccountSettings.tsx:77 -#: src/screens/Settings/AccountSettings.tsx:93 +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:211 +#: src/screens/Settings/AccountSettings.tsx:78 +#: src/screens/Settings/AccountSettings.tsx:98 msgid "Verify your email" msgstr "" @@ -8602,7 +8754,7 @@ msgstr "" msgid "Video failed to process" msgstr "" -#: src/Navigation.tsx:472 +#: src/Navigation.tsx:474 msgid "Video Feed" msgstr "" @@ -8616,11 +8768,11 @@ msgstr "" msgid "Video Games" msgstr "" -#: src/screens/VideoFeed/index.tsx:1041 +#: src/screens/VideoFeed/index.tsx:1049 msgid "Video is paused" msgstr "" -#: src/screens/VideoFeed/index.tsx:1041 +#: src/screens/VideoFeed/index.tsx:1049 msgid "Video is playing" msgstr "" @@ -8632,7 +8784,7 @@ msgstr "" msgid "Video settings" msgstr "" -#: src/view/com/composer/Composer.tsx:1703 +#: src/view/com/composer/Composer.tsx:1682 msgid "Video uploaded" msgstr "" @@ -8640,12 +8792,12 @@ msgstr "" msgid "Video: {0}" msgstr "" -#: src/view/screens/Profile.tsx:225 +#: src/view/screens/Profile.tsx:228 msgid "Videos" msgstr "" -#: src/view/com/composer/videos/SelectVideoBtn.tsx:60 -#: src/view/com/composer/videos/SelectVideoBtn.tsx:75 +#: src/view/com/composer/videos/SelectVideoBtn.tsx:42 +#: src/view/com/composer/videos/SelectVideoBtn.tsx:55 msgid "Videos must be less than 3 minutes long" msgstr "" @@ -8656,7 +8808,7 @@ msgstr "" #: src/components/ProfileCard.tsx:115 #: src/screens/Profile/components/ProfileFeedHeader.tsx:446 #: src/screens/Search/components/SearchProfileCard.tsx:36 -#: src/screens/VideoFeed/index.tsx:771 +#: src/screens/VideoFeed/index.tsx:779 #: src/view/com/notifications/NotificationFeedItem.tsx:489 msgid "View {0}'s profile" msgstr "" @@ -8678,8 +8830,8 @@ msgid "View debug entry" msgstr "" #: src/components/ReportDialog/SelectReportOptionView.tsx:137 -#: src/screens/VideoFeed/index.tsx:640 -#: src/screens/VideoFeed/index.tsx:658 +#: src/screens/VideoFeed/index.tsx:648 +#: src/screens/VideoFeed/index.tsx:666 msgid "View details" msgstr "" @@ -8798,7 +8950,7 @@ msgstr "" msgid "We couldn't find any results for that topic." msgstr "" -#: src/screens/Messages/Conversation.tsx:127 +#: src/screens/Messages/Conversation.tsx:130 msgid "We couldn't load this conversation" msgstr "" @@ -8822,6 +8974,10 @@ msgstr "" msgid "We recommend selecting at least two interests." msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:238 +msgid "We sent an email to <0>{0} containing a link. Please click on it to complete the email verification process." +msgstr "" + #: src/view/com/composer/state/video.ts:417 msgid "We were unable to determine if you are allowed to upload videos. Please try again." msgstr "" @@ -8842,6 +8998,10 @@ msgstr "" msgid "We will let you know when your account is ready." msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:247 +msgid "We'll send an email to <0>{0} containing a link. Please click on it to complete the email verification process." +msgstr "" + #: src/screens/Onboarding/StepInterests/index.tsx:134 msgid "We'll use this to help customize your experience." msgstr "" @@ -8871,7 +9031,7 @@ msgstr "" msgid "We're sorry, but your search could not be completed. Please try again in a few minutes." msgstr "" -#: src/view/com/composer/Composer.tsx:433 +#: src/view/com/composer/Composer.tsx:421 msgid "We're sorry! The post you are replying to has been deleted." msgstr "" @@ -8902,7 +9062,7 @@ msgstr "" #: src/view/com/auth/SplashScreen.tsx:38 #: src/view/com/auth/SplashScreen.web.tsx:99 -#: src/view/com/composer/Composer.tsx:753 +#: src/view/com/composer/Composer.tsx:732 msgid "What's up?" msgstr "" @@ -8931,7 +9091,7 @@ msgid "Who can verify?" msgstr "" #: src/screens/Home/NoFeedsPinned.tsx:79 -#: src/screens/Messages/ChatList.tsx:231 +#: src/screens/Messages/ChatList.tsx:232 #: src/screens/Messages/Inbox.tsx:176 msgid "Whoops!" msgstr "" @@ -8984,11 +9144,11 @@ msgstr "" msgid "Write a message" msgstr "" -#: src/view/com/composer/Composer.tsx:844 +#: src/view/com/composer/Composer.tsx:823 msgid "Write post" msgstr "" -#: src/view/com/composer/Composer.tsx:751 +#: src/view/com/composer/Composer.tsx:730 #: src/view/com/post-thread/PostThreadComposePrompt.tsx:69 msgid "Write your reply" msgstr "" @@ -9166,7 +9326,7 @@ msgstr "" msgid "You have muted this user" msgstr "" -#: src/screens/Messages/ChatList.tsx:274 +#: src/screens/Messages/ChatList.tsx:275 msgid "You have no conversations yet. Start one!" msgstr "" @@ -9191,11 +9351,15 @@ msgstr "" msgid "You have reached the end" msgstr "" +#: src/components/dialogs/EmailDialog/screens/Verify.tsx:187 +msgid "You have successfully verified your email address. You can close this dialog." +msgstr "" + #: src/lib/media/video/upload.shared.ts:56 msgid "You have temporarily reached the limit for video uploads. Please try again later." msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:245 +#: src/components/StarterPack/ProfileStarterPacks.tsx:263 msgid "You haven't created a starter pack yet!" msgstr "" @@ -9232,7 +9396,7 @@ msgstr "" msgid "You must be 13 years of age or older to create an account." msgstr "" -#: src/components/StarterPack/ProfileStarterPacks.tsx:328 +#: src/components/StarterPack/ProfileStarterPacks.tsx:334 msgid "You must be following at least seven other people to generate a starter pack." msgstr "" @@ -9248,7 +9412,7 @@ msgstr "" msgid "You must select at least one labeler for a report" msgstr "" -#: src/screens/Settings/components/Email2FAToggle.tsx:67 +#: src/components/dialogs/EmailDialog/screens/Manage2FA/index.tsx:23 msgid "You need to verify your email address before you can enable email 2FA." msgstr "" @@ -9347,6 +9511,10 @@ msgstr "" msgid "You've reached the end of your feed! Find some more accounts to follow." msgstr "" +#: src/lib/hooks/useCleanError.ts:58 +msgid "You've reached the maximum number of requests allowed. Please try again later." +msgstr "" + #: src/view/com/composer/state/video.ts:421 msgid "You've reached your daily limit for video uploads (too many bytes)" msgstr "" @@ -9355,7 +9523,7 @@ msgstr "" msgid "You've reached your daily limit for video uploads (too many videos)" msgstr "" -#: src/screens/VideoFeed/index.tsx:1092 +#: src/screens/VideoFeed/index.tsx:1100 msgid "You've run out of videos to watch. Maybe it's a good time to take a break?" msgstr "" @@ -9418,6 +9586,10 @@ msgstr "" msgid "Your email appears to be invalid." msgstr "" +#: src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx:61 +msgid "Your email has not yet been verified. Please verify your email in order to enjoy all the features of Bluesky." +msgstr "" + #: src/components/dialogs/VerifyEmailDialog.tsx:110 msgid "Your email has not yet been verified. This is an important security step which we recommend." msgstr "" @@ -9442,7 +9614,7 @@ msgstr "" msgid "Your full username will be <0>@{0}" msgstr "" -#: src/Navigation.tsx:393 +#: src/Navigation.tsx:395 #: src/screens/Search/modules/ExploreInterestsCard.tsx:67 #: src/screens/Settings/ContentAndMediaSettings.tsx:92 #: src/screens/Settings/ContentAndMediaSettings.tsx:95 @@ -9471,11 +9643,11 @@ msgstr "" msgid "Your password must be at least 8 characters long." msgstr "" -#: src/view/com/composer/Composer.tsx:493 +#: src/view/com/composer/Composer.tsx:481 msgid "Your post has been published" msgstr "" -#: src/view/com/composer/Composer.tsx:490 +#: src/view/com/composer/Composer.tsx:478 msgid "Your posts have been published" msgstr "" @@ -9487,7 +9659,7 @@ msgstr "" msgid "Your profile, posts, feeds, and lists will no longer be visible to other Bluesky users. You can reactivate your account at any time by logging in." msgstr "" -#: src/view/com/composer/Composer.tsx:492 +#: src/view/com/composer/Composer.tsx:480 msgid "Your reply has been published" msgstr "" From a6455b3de3f9c20b61e6791bb3f6c3ea29f65663 Mon Sep 17 00:00:00 2001 From: hailey Date: Thu, 8 May 2025 08:49:43 -0700 Subject: [PATCH 9/9] fix crash (#8348) --- modules/bottom-sheet/ios/SheetView.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/bottom-sheet/ios/SheetView.swift b/modules/bottom-sheet/ios/SheetView.swift index b6df8d7d2d..710c3c866f 100644 --- a/modules/bottom-sheet/ios/SheetView.swift +++ b/modules/bottom-sheet/ios/SheetView.swift @@ -145,7 +145,7 @@ class SheetView: ExpoView, UISheetPresentationControllerDelegate { func updateLayout() { // Allow updates either when identifiers match OR when prevLayoutDetentIdentifier is nil (first real content update) - if (self.prevLayoutDetentIdentifier == self.selectedDetentIdentifier || self.prevLayoutDetentIdentifier == nil), + if self.prevLayoutDetentIdentifier == self.selectedDetentIdentifier || self.prevLayoutDetentIdentifier == nil, let contentHeight = self.innerView?.subviews.first?.frame.size.height { self.sheetVc?.updateDetents(contentHeight: self.clampHeight(contentHeight), preventExpansion: self.preventExpansion) @@ -155,9 +155,15 @@ class SheetView: ExpoView, UISheetPresentationControllerDelegate { } func dismiss() { + guard let sheetVc = self.sheetVc else { + return + } + self.isClosing = true - self.sheetVc?.dismiss(animated: true) { [weak self] in - self?.destroy() + DispatchQueue.main.async { + sheetVc.dismiss(animated: true) { [weak self] in + self?.destroy() + } } }