From ad7d49ef8f6f579a4bf265c99c4d27075b8c437f Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 12 Dec 2025 20:32:06 +0200 Subject: [PATCH] [Contacts] Contacts matching flow (#9486) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add expo-contacts * add expo-sms * update copy * add basic settings screen * state machine, flow screen * phone input screen * otp screen * tweak spacing * resend code logic * add layoutanimationconfig * check availablility in settings screen * get contacts * matches screen * search, temp setup for matches UI * add a bunch of number parsing logic with libphonenumber * cast to looser type * rename sync to find FCF (Find Contacts Flow) * update geolocation hook * nicer design for settings screen * add completed state * up border contrast * update expo deps * add pending spinner * add country whitelist * add empty state screen * drop add more functionality * upload -> import * fix typo * fix permission string * copy updates * rm envelope icon * update sms copy * add inviteinfo component * woke is back * [Contacts] NUXes (#9515) * add a bunch of number parsing logic with libphonenumber * add banner nux * add announcement nux * native only nux * rm shitty animation * move isNative check * [Contacts] Onboarding step (#9489) * add a bunch of number parsing logic with libphonenumber * restructure onboarding to better support dynamic screens * integrate existing flow into onboarding * add intro step * lift state up to allow going back freely * gate onboarding by geo if unsupported country * add done button to standalone flow * [Contacts] Add `contact-match` notification type to feed (#9519) * add a bunch of number parsing logic with libphonenumber * add contact-joined notif type * update string, api package * Update NotificationFeedItem.tsx * Update NotificationFeedItem.tsx * Delete SyncContactsFlow.web.tsx * fix follow back btn for this case * [Contacts] API integration (#9487) * api integration for flow * copy tweak * tweaks after running it * wire up status page * rename toast * use api lib * rm temp code * maybe fix otp error * clear code on error/resend * add 1s delay to verify success * update package versions * Delete SyncContactsFlow.web.tsx * try and fix yarn.lock lint * even woker * fix uppercase friends * surfdude feedback Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * rm log * allow resend on invalid code * Update GetContacts.tsx * overwrite country code if possible * fix Apple's Best Feature * devenv latest * disable bounces * interactive keyboard dismiss * copy changes * national format * allow resending immediately * move success state down a bit * Update FindContactsSettings.tsx * [Contacts] More onboarding changes (#9491) * integrate existing flow into onboarding * refreshed onboarding styles, rm stepper * center content on web * Add back dismiss button for internal onboarding * Import sort --------- Co-authored-by: Eric Bailey * add matches query to shadow * add clockwise arrow * update status design, fix dismiss, fix queries * add metrics * add more comments * Update metrics.ts * refetch both queries on PTR * fix shadow state in matches page * reduce empty space at bottom * show contact info on matches * filter out contacts without numbers at an earlier stage * Error handling ✨ * get notifs working * filter out businesses * rm log * remove TODO from learn more links * try and exclude from web bundle --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> Co-authored-by: Eric Bailey --- app.config.js | 7 + ...otateClockwise_stroke2_corner0_rounded.svg | 1 + .../icons/contacts_filled_corner2_rounded.svg | 1 + .../contacts_stroke2_corner2_rounded.svg | 1 + ...gnifyingGlassX_stroke2_corner0_rounded.svg | 1 + ...ngGlassX_stroke2_corner0_rounded_large.svg | 1 + .../personX_stroke2_corner0_rounded_large.svg | 1 + assets/images/find_friends_illustration.webp | Bin 0 -> 171196 bytes .../find_friends_illustration_small.webp | Bin 0 -> 40962 bytes bskyweb/cmd/bskyweb/server.go | 1 - docs/build.md | 2 + package.json | 71 +- ...15.0.7.patch => expo-haptics+15.0.8.patch} | 0 ....patch.md => expo-haptics+15.0.8.patch.md} | 0 ....patch => expo-media-library+18.2.1.patch} | 0 ...4.patch => expo-modules-core+3.0.28.patch} | 0 ...h.md => expo-modules-core+3.0.28.patch.md} | 0 ...patch => expo-notifications+0.32.14.patch} | 0 ...md => expo-notifications+0.32.14.patch.md} | 0 ....0.12.patch => expo-updates+29.0.15.patch} | 21 - ...patch.md => expo-updates+29.0.15.patch.md} | 0 src/Navigation.tsx | 27 +- .../InternationalPhoneCodeSelect.tsx | 7 +- src/components/Layout/Header/index.tsx | 5 +- .../VideoEmbedInner/VideoFallback.tsx | 2 +- src/components/ProfileCard.tsx | 10 +- src/components/ProgressGuide/FollowDialog.tsx | 2 +- src/components/RichTextTag.tsx | 2 +- src/components/SearchError.tsx | 9 +- .../contacts/FindContactsBannerNUX.tsx | 107 ++ src/components/contacts/FindContactsFlow.tsx | 56 + .../contacts/FindContactsFlow.web.tsx | 3 + .../contacts/components/HeroImage.tsx | 33 + .../contacts/components/InviteInfo.tsx | 85 ++ .../contacts/components/OTPInput.tsx | 143 +++ src/components/contacts/contacts.ts | 100 ++ src/components/contacts/country-allowlist.ts | 37 + src/components/contacts/phone-number.ts | 162 +++ .../contacts/screens/GetContacts.tsx | 290 ++++++ .../contacts/screens/PhoneInput.tsx | 326 ++++++ .../contacts/screens/VerifyNumber.tsx | 386 +++++++ .../contacts/screens/ViewMatches.tsx | 683 ++++++++++++ src/components/contacts/state.ts | 198 ++++ src/components/dialogs/GifSelect.tsx | 2 +- .../dialogs/SearchablePeopleList.tsx | 2 +- .../dialogs/nuxs/FindContactsAnnouncement.tsx | 116 +++ src/components/dialogs/nuxs/index.tsx | 42 +- src/components/forms/SearchInput.tsx | 2 +- src/components/forms/TextField.tsx | 22 +- ...teCounterClockwise.tsx => ArrowRotate.tsx} | 6 + src/components/icons/Contacts.tsx | 9 + src/components/icons/MagnifyingGlass.tsx | 14 + src/components/icons/MagnifyingGlass2.tsx | 5 - src/components/icons/Person.tsx | 5 + .../intents/VerifyEmailIntentDialog.tsx | 2 +- .../moderation/ReportDialog/index.tsx | 2 +- src/lib/international-telephone-codes.ts | 120 ++- src/lib/routes/types.ts | 2 + src/lib/statsig/gates.ts | 1 + src/logger/metrics.ts | 89 ++ src/routes.ts | 2 + src/screens/FindContactsFlowScreen.tsx | 77 ++ src/screens/Messages/ChatList.tsx | 2 +- src/screens/Messages/Inbox.tsx | 2 +- src/screens/Onboarding/Layout.tsx | 202 ++-- .../Onboarding/StepFindContacts/index.tsx | 60 ++ .../Onboarding/StepFindContacts/index.web.tsx | 3 + .../StepFindContactsIntro/index.tsx | 88 ++ .../StepFindContactsIntro/index.web.tsx | 3 + src/screens/Onboarding/StepFinished/index.tsx | 114 +- .../Onboarding/StepInterests/index.tsx | 29 +- src/screens/Onboarding/StepProfile/index.tsx | 46 +- .../StepSuggestedAccounts/index.tsx | 37 +- .../StepSuggestedStarterpacks/index.tsx | 30 +- src/screens/Onboarding/index.tsx | 121 +-- src/screens/Onboarding/state.ts | 180 +++- .../PostThread/components/ThreadError.tsx | 2 +- .../Search/components/ModuleHeader.tsx | 2 +- src/screens/Settings/FindContactsSettings.tsx | 562 ++++++++++ src/screens/Settings/Settings.tsx | 14 + src/screens/Settings/components/OTAInfo.tsx | 2 +- src/state/cache/profile-shadow.ts | 2 + src/state/queries/find-contacts.ts | 128 +++ src/state/queries/notifications/feed.ts | 5 +- src/state/queries/notifications/types.ts | 1 + src/state/queries/notifications/util.ts | 3 +- src/state/queries/nuxs/definitions.ts | 12 + .../notifications/NotificationFeedItem.tsx | 34 +- src/view/com/profile/ProfileFollows.tsx | 2 + src/view/com/profile/ProfileMenu.tsx | 2 +- src/view/com/util/error/ErrorScreen.tsx | 2 +- src/view/screens/Storybook/Admonitions.tsx | 2 +- src/view/screens/Storybook/Menus.tsx | 2 +- src/view/shell/Drawer.tsx | 2 +- src/view/shell/bottom-bar/BottomBar.tsx | 2 +- src/view/shell/bottom-bar/BottomBarWeb.tsx | 2 +- src/view/shell/desktop/LeftNav.tsx | 2 +- yarn.lock | 982 +++++++++--------- 98 files changed, 4906 insertions(+), 1079 deletions(-) create mode 100644 assets/icons/arrowRotateClockwise_stroke2_corner0_rounded.svg create mode 100644 assets/icons/contacts_filled_corner2_rounded.svg create mode 100644 assets/icons/contacts_stroke2_corner2_rounded.svg create mode 100644 assets/icons/magnifyingGlassX_stroke2_corner0_rounded.svg create mode 100644 assets/icons/magnifyingGlassX_stroke2_corner0_rounded_large.svg create mode 100644 assets/icons/personX_stroke2_corner0_rounded_large.svg create mode 100644 assets/images/find_friends_illustration.webp create mode 100644 assets/images/find_friends_illustration_small.webp rename patches/{expo-haptics+15.0.7.patch => expo-haptics+15.0.8.patch} (100%) rename patches/{expo-haptics+15.0.7.patch.md => expo-haptics+15.0.8.patch.md} (100%) rename patches/{expo-media-library+18.2.0.patch => expo-media-library+18.2.1.patch} (100%) rename patches/{expo-modules-core+3.0.24.patch => expo-modules-core+3.0.28.patch} (100%) rename patches/{expo-modules-core+3.0.24.patch.md => expo-modules-core+3.0.28.patch.md} (100%) rename patches/{expo-notifications+0.32.12.patch => expo-notifications+0.32.14.patch} (100%) rename patches/{expo-notifications+0.32.12.patch.md => expo-notifications+0.32.14.patch.md} (100%) rename patches/{expo-updates+29.0.12.patch => expo-updates+29.0.15.patch} (51%) rename patches/{expo-updates+29.0.12.patch.md => expo-updates+29.0.15.patch.md} (100%) create mode 100644 src/components/contacts/FindContactsBannerNUX.tsx create mode 100644 src/components/contacts/FindContactsFlow.tsx create mode 100644 src/components/contacts/FindContactsFlow.web.tsx create mode 100644 src/components/contacts/components/HeroImage.tsx create mode 100644 src/components/contacts/components/InviteInfo.tsx create mode 100644 src/components/contacts/components/OTPInput.tsx create mode 100644 src/components/contacts/contacts.ts create mode 100644 src/components/contacts/country-allowlist.ts create mode 100644 src/components/contacts/phone-number.ts create mode 100644 src/components/contacts/screens/GetContacts.tsx create mode 100644 src/components/contacts/screens/PhoneInput.tsx create mode 100644 src/components/contacts/screens/VerifyNumber.tsx create mode 100644 src/components/contacts/screens/ViewMatches.tsx create mode 100644 src/components/contacts/state.ts create mode 100644 src/components/dialogs/nuxs/FindContactsAnnouncement.tsx rename src/components/icons/{ArrowRotateCounterClockwise.tsx => ArrowRotate.tsx} (52%) create mode 100644 src/components/icons/Contacts.tsx delete mode 100644 src/components/icons/MagnifyingGlass2.tsx create mode 100644 src/screens/FindContactsFlowScreen.tsx create mode 100644 src/screens/Onboarding/StepFindContacts/index.tsx create mode 100644 src/screens/Onboarding/StepFindContacts/index.web.tsx create mode 100644 src/screens/Onboarding/StepFindContactsIntro/index.tsx create mode 100644 src/screens/Onboarding/StepFindContactsIntro/index.web.tsx create mode 100644 src/screens/Settings/FindContactsSettings.tsx create mode 100644 src/state/queries/find-contacts.ts diff --git a/app.config.js b/app.config.js index 54c6edc8ce..ca417206bf 100644 --- a/app.config.js +++ b/app.config.js @@ -389,6 +389,13 @@ module.exports = function (_config) { ], ['expo-screen-orientation', {initialOrientation: 'PORTRAIT_UP'}], ['expo-location'], + [ + 'expo-contacts', + { + contactsPermission: + 'I agree to allow Bluesky to use my contacts for friend discovery until I opt out.', + }, + ], ].filter(Boolean), extra: { eas: { diff --git a/assets/icons/arrowRotateClockwise_stroke2_corner0_rounded.svg b/assets/icons/arrowRotateClockwise_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..6cb05284d9 --- /dev/null +++ b/assets/icons/arrowRotateClockwise_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/contacts_filled_corner2_rounded.svg b/assets/icons/contacts_filled_corner2_rounded.svg new file mode 100644 index 0000000000..4f116ca10b --- /dev/null +++ b/assets/icons/contacts_filled_corner2_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/contacts_stroke2_corner2_rounded.svg b/assets/icons/contacts_stroke2_corner2_rounded.svg new file mode 100644 index 0000000000..d7ba9a150b --- /dev/null +++ b/assets/icons/contacts_stroke2_corner2_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/magnifyingGlassX_stroke2_corner0_rounded.svg b/assets/icons/magnifyingGlassX_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..a21615b5f0 --- /dev/null +++ b/assets/icons/magnifyingGlassX_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/magnifyingGlassX_stroke2_corner0_rounded_large.svg b/assets/icons/magnifyingGlassX_stroke2_corner0_rounded_large.svg new file mode 100644 index 0000000000..e39b483268 --- /dev/null +++ b/assets/icons/magnifyingGlassX_stroke2_corner0_rounded_large.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/personX_stroke2_corner0_rounded_large.svg b/assets/icons/personX_stroke2_corner0_rounded_large.svg new file mode 100644 index 0000000000..d77d3763b3 --- /dev/null +++ b/assets/icons/personX_stroke2_corner0_rounded_large.svg @@ -0,0 +1 @@ + diff --git a/assets/images/find_friends_illustration.webp b/assets/images/find_friends_illustration.webp new file mode 100644 index 0000000000000000000000000000000000000000..9074282ea6239219cc8cc83a300b4eeb13f6e7b7 GIT binary patch literal 171196 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

2U9`%CSOuP;S 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

    b@ws94txYj24gEfbc(`vrWMeFj)z`2) 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 G0001xR{V7U literal 0 HcmV?d00001 diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 790f211eed..5b81396262 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -308,7 +308,6 @@ func serve(cctx *cli.Context) error { e.GET("/settings/notifications/reposts-on-reposts", server.WebGeneric) e.GET("/settings/notifications/activity", server.WebGeneric) e.GET("/settings/notifications/miscellaneous", server.WebGeneric) - e.GET("/settings/app-icon", server.WebGeneric) e.GET("/sys/debug", server.WebGeneric) e.GET("/sys/debug-mod", server.WebGeneric) e.GET("/sys/log", server.WebGeneric) diff --git a/docs/build.md b/docs/build.md index fe95bb989a..bcae2ece09 100644 --- a/docs/build.md +++ b/docs/build.md @@ -111,6 +111,8 @@ This is NOT required for app development but if you also want to develop the Blu - Start the docker daemon (on MacOS this entails starting the Docker Desktop app) - Launch a Postgres database on port 5432 - `cd packages/dev-env && pnpm start` + +Run the account with the AppView proxy DID passed in as an environment variable: `EXPO_PUBLIC_BLUESKY_PROXY_DID=did:plc:dw4kbjf5mn7nhenabiqpkyh3 yarn start` Then, when logging in or creating an account, point it to the localhost port of the devserver. diff --git a/package.json b/package.json index e26f1645b2..d8f3dee7f7 100644 --- a/package.json +++ b/package.json @@ -72,10 +72,10 @@ "icons:optimize": "svgo -f ./assets/icons" }, "dependencies": { - "@atproto/api": "^0.18.6", + "@atproto/api": "^0.18.8", "@bitdrift/react-native": "^0.6.8", "@braintree/sanitize-url": "^6.0.2", - "@bsky.app/alf": "^0.1.5", + "@bsky.app/alf": "^0.1.6", "@bsky.app/react-native-mmkv": "2.12.5", "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", "@emoji-mart/react": "^1.1.1", @@ -132,43 +132,46 @@ "emoji-mart": "^5.5.2", "emoji-regex": "^10.4.0", "eventemitter3": "^5.0.1", - "expo": "^54.0.22", - "expo-application": "~7.0.7", - "expo-blur": "~15.0.7", - "expo-build-properties": "~1.0.9", - "expo-camera": "~17.0.9", - "expo-clipboard": "~8.0.7", - "expo-dev-client": "~6.0.16", - "expo-device": "~8.0.9", - "expo-file-system": "~19.0.17", - "expo-font": "~14.0.9", - "expo-haptics": "~15.0.7", - "expo-image": "~3.0.10", + "expo": "^54.0.27", + "expo-application": "~7.0.8", + "expo-blur": "~15.0.8", + "expo-build-properties": "~1.0.10", + "expo-camera": "~17.0.10", + "expo-clipboard": "~8.0.8", + "expo-contacts": "^15.0.10", + "expo-dev-client": "~6.0.20", + "expo-device": "~8.0.10", + "expo-file-system": "~19.0.20", + "expo-font": "~14.0.10", + "expo-haptics": "~15.0.8", + "expo-image": "~3.0.11", "expo-image-crop-tool": "^0.4.0", - "expo-image-manipulator": "~14.0.7", - "expo-image-picker": "~17.0.8", - "expo-intent-launcher": "~13.0.7", - "expo-keep-awake": "^15.0.7", - "expo-linear-gradient": "~15.0.7", - "expo-linking": "~8.0.8", - "expo-localization": "~17.0.7", - "expo-location": "~19.0.7", - "expo-media-library": "~18.2.0", - "expo-notifications": "~0.32.12", - "expo-screen-orientation": "~9.0.7", - "expo-sharing": "~14.0.7", - "expo-splash-screen": "~31.0.10", - "expo-system-ui": "~6.0.8", - "expo-task-manager": "~14.0.8", - "expo-updates": "~29.0.12", - "expo-video": "~3.0.13", - "expo-web-browser": "~15.0.9", + "expo-image-manipulator": "~14.0.8", + "expo-image-picker": "~17.0.9", + "expo-intent-launcher": "~13.0.8", + "expo-keep-awake": "~15.0.8", + "expo-linear-gradient": "~15.0.8", + "expo-linking": "~8.0.10", + "expo-localization": "~17.0.8", + "expo-location": "~19.0.8", + "expo-media-library": "~18.2.1", + "expo-notifications": "~0.32.14", + "expo-screen-orientation": "~9.0.8", + "expo-sharing": "~14.0.8", + "expo-sms": "^14.0.7", + "expo-splash-screen": "~31.0.12", + "expo-system-ui": "~6.0.9", + "expo-task-manager": "~14.0.9", + "expo-updates": "~29.0.14", + "expo-video": "~3.0.15", + "expo-web-browser": "~15.0.10", "fast-text-encoding": "^1.0.6", "history": "^5.3.0", "hls.js": "^1.6.2", "js-sha256": "^0.9.0", "jwt-decode": "^4.0.0", "lande": "^1.0.10", + "libphonenumber-js": "^1.12.31", "lodash.chunk": "^4.2.0", "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", @@ -222,7 +225,7 @@ "zod": "^3.20.2" }, "devDependencies": { - "@atproto/dev-env": "^0.3.193", + "@atproto/dev-env": "^0.3.196", "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@babel/runtime": "^7.26.0", @@ -264,7 +267,7 @@ "husky": "^8.0.3", "is-ci": "^3.0.1", "jest": "^29.7.0", - "jest-expo": "^54.0.14", + "jest-expo": "~54.0.14", "jest-junit": "^16.0.0", "lint-staged": "^13.2.3", "lockfile-lint": "^4.14.0", diff --git a/patches/expo-haptics+15.0.7.patch b/patches/expo-haptics+15.0.8.patch similarity index 100% rename from patches/expo-haptics+15.0.7.patch rename to patches/expo-haptics+15.0.8.patch diff --git a/patches/expo-haptics+15.0.7.patch.md b/patches/expo-haptics+15.0.8.patch.md similarity index 100% rename from patches/expo-haptics+15.0.7.patch.md rename to patches/expo-haptics+15.0.8.patch.md diff --git a/patches/expo-media-library+18.2.0.patch b/patches/expo-media-library+18.2.1.patch similarity index 100% rename from patches/expo-media-library+18.2.0.patch rename to patches/expo-media-library+18.2.1.patch diff --git a/patches/expo-modules-core+3.0.24.patch b/patches/expo-modules-core+3.0.28.patch similarity index 100% rename from patches/expo-modules-core+3.0.24.patch rename to patches/expo-modules-core+3.0.28.patch diff --git a/patches/expo-modules-core+3.0.24.patch.md b/patches/expo-modules-core+3.0.28.patch.md similarity index 100% rename from patches/expo-modules-core+3.0.24.patch.md rename to patches/expo-modules-core+3.0.28.patch.md diff --git a/patches/expo-notifications+0.32.12.patch b/patches/expo-notifications+0.32.14.patch similarity index 100% rename from patches/expo-notifications+0.32.12.patch rename to patches/expo-notifications+0.32.14.patch diff --git a/patches/expo-notifications+0.32.12.patch.md b/patches/expo-notifications+0.32.14.patch.md similarity index 100% rename from patches/expo-notifications+0.32.12.patch.md rename to patches/expo-notifications+0.32.14.patch.md diff --git a/patches/expo-updates+29.0.12.patch b/patches/expo-updates+29.0.15.patch similarity index 51% rename from patches/expo-updates+29.0.12.patch rename to patches/expo-updates+29.0.15.patch index 7f198e96ef..06841f0be0 100644 --- a/patches/expo-updates+29.0.12.patch +++ b/patches/expo-updates+29.0.15.patch @@ -1,24 +1,3 @@ -diff --git a/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/LoaderTask.kt b/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/LoaderTask.kt -index e9a8e3d..3c684e0 100644 ---- a/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/LoaderTask.kt -+++ b/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/LoaderTask.kt -@@ -296,14 +296,13 @@ class LoaderTask( - ) { - try { - val embeddedLoader = EmbeddedLoader(context, configuration, logger, database, directory) -- val result = embeddedLoader.load { updateResponse -> -+ embeddedLoader.load { _ -> - Loader.OnUpdateResponseLoadedResult(shouldDownloadManifestIfPresentInResponse = true) - } -- launcher.launch(database) - } catch (e: Exception) { - logger.error("Unexpected error copying embedded update", e, UpdatesErrorCode.Unknown) -- launcher.launch(database) - } -+ launcher.launch(database) - } else { - launcher.launch(database) - } diff --git a/node_modules/expo-updates/ios/EXUpdates/Update/ExpoUpdatesUpdate.swift b/node_modules/expo-updates/ios/EXUpdates/Update/ExpoUpdatesUpdate.swift index 68086bd..78c7761 100644 --- a/node_modules/expo-updates/ios/EXUpdates/Update/ExpoUpdatesUpdate.swift diff --git a/patches/expo-updates+29.0.12.patch.md b/patches/expo-updates+29.0.15.patch.md similarity index 100% rename from patches/expo-updates+29.0.12.patch.md rename to patches/expo-updates+29.0.15.patch.md diff --git a/src/Navigation.tsx b/src/Navigation.tsx index fa33a9d56c..3e68bd07df 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -18,7 +18,9 @@ import { } from '@react-navigation/native' import {timeout} from '#/lib/async/timeout' +import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' +import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import { getNotificationPayload, type NotificationPayload, @@ -45,10 +47,12 @@ import {logger} from '#/logger' import {isNative, isWeb} from '#/platform/detection' import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useSession} from '#/state/session' +import {useLoggedOutViewControls} from '#/state/shell/logged-out' import { shouldRequestEmailConfirmation, snoozeEmailConfirmationPrompt, } from '#/state/shell/reminders' +import {useCloseAllActiveElements} from '#/state/util' import {CommunityGuidelinesScreen} from '#/view/screens/CommunityGuidelines' import {CopyrightPolicyScreen} from '#/view/screens/CopyrightPolicy' import {DebugModScreen} from '#/view/screens/DebugMod' @@ -71,6 +75,7 @@ import {BottomBar} from '#/view/shell/bottom-bar/BottomBar' import {createNativeStackNavigatorWithAuth} from '#/view/shell/createNativeStackNavigatorWithAuth' import {BookmarksScreen} from '#/screens/Bookmarks' import {SharedPreferencesTesterScreen} from '#/screens/E2E/SharedPreferencesTesterScreen' +import {FindContactsFlowScreen} from '#/screens/FindContactsFlowScreen' import HashtagScreen from '#/screens/Hashtag' import {LogScreen} from '#/screens/Log' import {MessagesScreen} from '#/screens/Messages/ChatList' @@ -102,6 +107,7 @@ import {AppIconSettingsScreen} from '#/screens/Settings/AppIconSettings' import {AppPasswordsScreen} from '#/screens/Settings/AppPasswords' import {ContentAndMediaSettingsScreen} from '#/screens/Settings/ContentAndMediaSettings' import {ExternalMediaPreferencesScreen} from '#/screens/Settings/ExternalMediaPreferences' +import {FindContactsSettingsScreen} from '#/screens/Settings/FindContactsSettings' import {FollowingFeedPreferencesScreen} from '#/screens/Settings/FollowingFeedPreferences' import {InterestsSettingsScreen} from '#/screens/Settings/InterestsSettings' import {LanguageSettingsScreen} from '#/screens/Settings/LanguageSettings' @@ -134,10 +140,6 @@ import { } from '#/components/dialogs/EmailDialog' import {router} from '#/routes' import {Referrer} from '../modules/expo-bluesky-swiss-army' -import {useAccountSwitcher} from './lib/hooks/useAccountSwitcher' -import {useNonReactiveCallback} from './lib/hooks/useNonReactiveCallback' -import {useLoggedOutViewControls} from './state/shell/logged-out' -import {useCloseAllActiveElements} from './state/util' const navigationRef = createNavigationContainerRef() @@ -416,6 +418,14 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) { requireAuth: true, }} /> + FindContactsSettingsScreen} + options={{ + title: title(msg`Find Contacts`), + requireAuth: true, + }} + /> NotificationSettingsScreen} @@ -609,6 +619,15 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) { requireAuth: true, }} /> + FindContactsFlowScreen} + options={{ + title: title(msg`Find Contacts`), + requireAuth: true, + gestureEnabled: false, + }} + /> ) } diff --git a/src/components/InternationalPhoneCodeSelect.tsx b/src/components/InternationalPhoneCodeSelect.tsx index f42f4a21a3..0cfd4d6b5c 100644 --- a/src/components/InternationalPhoneCodeSelect.tsx +++ b/src/components/InternationalPhoneCodeSelect.tsx @@ -4,6 +4,7 @@ import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import { + type CountryCode, getDefaultCountry, INTERNATIONAL_TELEPHONE_CODES, } from '#/lib/international-telephone-codes' @@ -23,8 +24,8 @@ export function InternationalPhoneCodeSelect({ value, onChange, }: { - value?: string - onChange: (value: string) => void + value?: CountryCode + onChange: (value: CountryCode) => void }) { const {_, i18n} = useLingui() const location = useGeolocation() @@ -62,7 +63,7 @@ export function InternationalPhoneCodeSelect({ }, [value, items]) return ( - + void}> {selected => ( diff --git a/src/components/Layout/Header/index.tsx b/src/components/Layout/Header/index.tsx index 762223fc35..59c360fa18 100644 --- a/src/components/Layout/Header/index.tsx +++ b/src/components/Layout/Header/index.tsx @@ -38,7 +38,7 @@ export function Outer({ }: { children: React.ReactNode noBottomBorder?: boolean - headerRef?: React.MutableRefObject + headerRef?: React.RefObject sticky?: boolean }) { const t = useTheme() @@ -129,6 +129,7 @@ export function BackButton({onPress, style, ...props}: Partial) { size="small" variant="ghost" color="secondary" + shape="round" onPress={onPressBack} hitSlop={HITSLOP_30} style={[ @@ -183,7 +184,7 @@ export function TitleText({ +}) { const t = useTheme() const handle = sanitizeHandle(profile.handle, '@') return ( {handle} diff --git a/src/components/ProgressGuide/FollowDialog.tsx b/src/components/ProgressGuide/FollowDialog.tsx index 4d1d27d15d..d1017c46f6 100644 --- a/src/components/ProgressGuide/FollowDialog.tsx +++ b/src/components/ProgressGuide/FollowDialog.tsx @@ -31,7 +31,7 @@ import { import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {useInteractionState} from '#/components/hooks/useInteractionState' -import {MagnifyingGlass2_Stroke2_Corner0_Rounded as SearchIcon} from '#/components/icons/MagnifyingGlass2' +import {MagnifyingGlass_Stroke2_Corner0_Rounded as SearchIcon} from '#/components/icons/MagnifyingGlass' import {PersonGroup_Stroke2_Corner2_Rounded as PersonGroupIcon} from '#/components/icons/Person' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {boostInterests, InterestTabs} from '#/components/InterestTabs' diff --git a/src/components/RichTextTag.tsx b/src/components/RichTextTag.tsx index a24ebb15c2..4f1af6e1ea 100644 --- a/src/components/RichTextTag.tsx +++ b/src/components/RichTextTag.tsx @@ -12,7 +12,7 @@ import { useRemoveMutedWordsMutation, useUpsertMutedWordsMutation, } from '#/state/queries/preferences' -import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2' +import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass' import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute' import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person' import { diff --git a/src/components/SearchError.tsx b/src/components/SearchError.tsx index d478ac8850..4c9b402612 100644 --- a/src/components/SearchError.tsx +++ b/src/components/SearchError.tsx @@ -1,10 +1,9 @@ import {View} from 'react-native' -import {usePalette} from '#/lib/hooks/usePalette' -import {atoms as a, useBreakpoints} from '#/alf' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' import * as Layout from '#/components/Layout' import {Text} from '#/components/Typography' -import {TimesLarge_Stroke2_Corner0_Rounded} from './icons/Times' export function SearchError({ title, @@ -14,7 +13,7 @@ export function SearchError({ children?: React.ReactNode }) { const {gtMobile} = useBreakpoints() - const pal = usePalette('default') + const t = useTheme() return ( @@ -27,7 +26,7 @@ export function SearchError({ paddingVertical: 150, }, ]}> - + + + { + logger.metric('contacts:nux:bannerPressed', {}) + }} + style={[ + a.w_full, + a.rounded_xl, + a.curve_continuous, + a.overflow_hidden, + ]}> + + + + + Import contacts to find your friends + + + + + + + + ) +} +function useInternalState() { + const {nux} = useNux(Nux.FindContactsDismissibleBanner) + const {mutate: save, variables} = useSaveNux() + const hidden = !!variables + + const visible = useMemo(() => { + if (isWeb) return false + if (hidden) return false + if (nux && nux.completed) return false + return true + }, [hidden, nux]) + + const close = () => { + save({ + id: Nux.FindContactsDismissibleBanner, + completed: true, + data: undefined, + }) + logger.metric('contacts:nux:bannerDismissed', {}) + } + + return {visible, close} +} diff --git a/src/components/contacts/FindContactsFlow.tsx b/src/components/contacts/FindContactsFlow.tsx new file mode 100644 index 0000000000..66f52bbd0a --- /dev/null +++ b/src/components/contacts/FindContactsFlow.tsx @@ -0,0 +1,56 @@ +import {GetContacts} from './screens/GetContacts' +import {PhoneInput} from './screens/PhoneInput' +import {VerifyNumber} from './screens/VerifyNumber' +import {ViewMatches} from './screens/ViewMatches' +import {type Action, FindContactsGoBackContext, type State} from './state' + +export function FindContactsFlow({ + state, + dispatch, + onBack, + onCancel, + context = 'Standalone', +}: { + state: State + dispatch: React.ActionDispatch<[Action]> + onBack?: () => void + onCancel: () => void + context: 'Onboarding' | 'Standalone' +}) { + return ( + + {state.step === '1: phone input' && ( + + )} + {state.step === '2: verify number' && ( + + )} + {state.step === '3: get contacts' && ( + + )} + {state.step === '4: view matches' && ( + + )} + + ) +} diff --git a/src/components/contacts/FindContactsFlow.web.tsx b/src/components/contacts/FindContactsFlow.web.tsx new file mode 100644 index 0000000000..9def7df16b --- /dev/null +++ b/src/components/contacts/FindContactsFlow.web.tsx @@ -0,0 +1,3 @@ +export function FindContactsFlow() { + throw new Error('FindContactsFlow is not available on web') +} diff --git a/src/components/contacts/components/HeroImage.tsx b/src/components/contacts/components/HeroImage.tsx new file mode 100644 index 0000000000..5e710fd9ae --- /dev/null +++ b/src/components/contacts/components/HeroImage.tsx @@ -0,0 +1,33 @@ +import {View} from 'react-native' +import {Image} from 'expo-image' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {atoms as a, useTheme} from '#/alf' + +export function ContactsHeroImage() { + const t = useTheme() + const {_} = useLingui() + + return ( + + {_( + + ) +} diff --git a/src/components/contacts/components/InviteInfo.tsx b/src/components/contacts/components/InviteInfo.tsx new file mode 100644 index 0000000000..54100a2a01 --- /dev/null +++ b/src/components/contacts/components/InviteInfo.tsx @@ -0,0 +1,85 @@ +import {type StyleProp, type TextStyle} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {HITSLOP_20} from '#/lib/constants' +import {android, atoms as a} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import {CircleInfo_Stroke2_Corner0_Rounded as InfoIcon} from '#/components/icons/CircleInfo' +import {Text} from '#/components/Typography' + +export function InviteInfo({ + iconStyle, + iconOffset = 0, +}: { + iconStyle: StyleProp + /** + * Adjust the vertical position of the icon via `translateY` + * + * @platform android + */ + iconOffset?: number +}) { + const {_} = useLingui() + const control = Dialog.useDialogControl() + + const style = [a.text_md, a.leading_snug, a.mt_xs] + + return ( + <> + + + + + + + Invite Friends + + + + It looks like some of your contacts have not tried to find you + here yet. You can personally invite them by customizing a draft + message we will provide. + + + + How it works: + + + • Choose who to invite + + + • Personalize the message + + + • Send the message from your phone + + + •{' '} + + We don't store your friends' phone numbers or send any messages + + + + + + + + ) +} diff --git a/src/components/contacts/components/OTPInput.tsx b/src/components/contacts/components/OTPInput.tsx new file mode 100644 index 0000000000..da68bf8d5a --- /dev/null +++ b/src/components/contacts/components/OTPInput.tsx @@ -0,0 +1,143 @@ +import {useRef, useState} from 'react' +import { + Pressable, + TextInput, + type TextInputSelectionChangeEvent, + View, +} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {mergeRefs} from '#/lib/merge-refs' +import {isAndroid, isIOS} from '#/platform/detection' +import {atoms as a, ios, platform, useTheme} from '#/alf' +import {useInteractionState} from '#/components/hooks/useInteractionState' +import {Text} from '#/components/Typography' + +export function OTPInput({ + label, + value, + onChange, + ref, + numberOfDigits = 6, + onComplete, +}: { + label: string + value: string + onChange: (text: string) => void + ref?: React.Ref + numberOfDigits?: number + onComplete?: (code: string) => void +}) { + const t = useTheme() + const {_} = useLingui() + const innerRef = useRef(null) + const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() + const [selection, setSelection] = useState({start: 0, end: 0}) + + const onChangeText = (text: string) => { + // only numbers + text = text.replace(/[^0-9]/g, '') + text = text.slice(0, numberOfDigits) + onChange(text) + if (text.length === numberOfDigits) { + onComplete?.(text) + innerRef.current?.blur() + } + } + + const onSelectionChange = (evt: TextInputSelectionChangeEvent) => { + setSelection(evt.nativeEvent.selection) + } + + return ( + { + innerRef.current?.focus() + innerRef.current?.clear() + }}> + + {[...value.padEnd(numberOfDigits, ' ')].map((digit, index) => { + const selected = focused + ? selection.start === selection.end + ? selection.start === index + : index >= selection.start && index < selection.end + : false + + return ( + + + {digit} + + + ) + })} + + setTimeout(() => innerRef.current?.focus(), 100))} + autoFocus={isAndroid} + accessible + accessibilityLabel={label} + accessibilityHint="" + accessibilityRole="text" + ref={mergeRefs(ref ? [ref, innerRef] : [innerRef])} + value={value} + onChangeText={onChangeText} + onSelectionChange={onSelectionChange} + keyboardAppearance={t.scheme} + inputMode="numeric" + keyboardType="number-pad" + textContentType="oneTimeCode" + autoComplete={platform({ + android: 'sms-otp', + ios: 'one-time-code', + })} + onFocus={onFocus} + onBlur={onBlur} + maxLength={numberOfDigits} + style={[ + a.absolute, + a.inset_0, + // roughly vibe align the characters + // with the visible ones so that + // moving the caret via long press + // still kinda sorta works + { + fontVariant: ['tabular-nums'], + textAlignVertical: 'center', + letterSpacing: 24, + fontSize: 60, + paddingLeft: 6, + }, + platform({ + // completely transparent inputs on iOS cannot be pasted into + ios: {opacity: 0.02, color: 'transparent'}, + android: {opacity: 0}, + }), + ]} + caretHidden={isIOS} + clearTextOnFocus + /> + + ) +} diff --git a/src/components/contacts/contacts.ts b/src/components/contacts/contacts.ts new file mode 100644 index 0000000000..205c2ccb49 --- /dev/null +++ b/src/components/contacts/contacts.ts @@ -0,0 +1,100 @@ +import {type AppBskyContactDefs} from '@atproto/api' + +import {type CountryCode} from '#/lib/international-telephone-codes' +import {normalizePhoneNumber} from './phone-number' +import {type Contact, type Match} from './state' + +/** + * Filters out contacts that do not have any associated phone numbers, + * as well as businesses + */ +export function contactsWithPhoneNumbersOnly(contacts: Contact[]) { + return contacts.filter( + contact => + contact.phoneNumbers && + contact.phoneNumbers.length > 0 && + contact.contactType !== 'company', + ) +} + +/** + * Takes the raw contact book and returns a plain list of numbers in E.164 format, along + * with a mapping to retrieve the contact ID when we get the results back. + * + * `countryCode` is used as a fallback for local numbers that don't have a country code associated with them. + * I'm making the assumption that most local numbers in someone's phone book will be the same as theirs. + */ +export function normalizeContactBook( + contacts: Contact[], + countryCode: CountryCode, + ownNumber: string, +): { + phoneNumbers: string[] + indexToContactId: Map +} { + const phoneNumbers: string[] = [] + const indexToContactId = new Map() + + for (const contact of contacts) { + for (const number of contact.phoneNumbers ?? []) { + let rawNumber: string + + if (number.number) { + rawNumber = number.number + } else if (number.digits) { + rawNumber = number.digits + } else { + continue + } + + const normalized = normalizePhoneNumber( + rawNumber, + number.countryCode, + countryCode, + ) + if (normalized === null) continue + + // skip if it's your own number + if (normalized === ownNumber) continue + + phoneNumbers.push(normalized) + indexToContactId.set(phoneNumbers.length - 1, contact.id) + } + } + + return { + phoneNumbers, + indexToContactId, + } +} + +export function filterMatchedNumbers( + contacts: Contact[], + results: AppBskyContactDefs.MatchAndContactIndex[], + mapping: Map, +) { + const filteredIds = new Set() + + for (const result of results) { + const id = mapping.get(result.contactIndex) + if (id !== undefined) { + filteredIds.add(id) + } + } + + return contacts.filter(contact => !filteredIds.has(contact.id)) +} + +export function getMatchedContacts( + contacts: Contact[], + results: AppBskyContactDefs.MatchAndContactIndex[], + mapping: Map, +): Array { + const contactsById = new Map(contacts.map(c => [c.id, c])) + + return results.map(result => { + const id = mapping.get(result.contactIndex) + const contact = id !== undefined ? contactsById.get(id) : undefined + return {profile: result.match, contact} + }) +} diff --git a/src/components/contacts/country-allowlist.ts b/src/components/contacts/country-allowlist.ts new file mode 100644 index 0000000000..97d3d44587 --- /dev/null +++ b/src/components/contacts/country-allowlist.ts @@ -0,0 +1,37 @@ +import {type CountryCode} from '#/lib/international-telephone-codes' +import {IS_DEV} from '#/env' +import {useGeolocation} from '#/geolocation' + +const FIND_CONTACTS_FEATURE_COUNTRY_ALLOWLIST = [ + 'US', + 'GB', + 'JP', + 'CA', + 'DE', + 'FR', + 'ES', + 'BR', + 'KR', + 'NL', + 'AU', + 'SE', + 'IT', +] satisfies CountryCode[] as string[] + +export function isFindContactsFeatureEnabled(countryCode: string): boolean { + return FIND_CONTACTS_FEATURE_COUNTRY_ALLOWLIST.includes( + countryCode.toUpperCase(), + ) +} + +export function useIsFindContactsFeatureEnabledBasedOnGeolocation() { + const location = useGeolocation() + + if (IS_DEV) return true + + // they can try, by they'll need a phone number + // from one of the allowlisted countries + if (!location.countryCode) return true + + return isFindContactsFeatureEnabled(location.countryCode) +} diff --git a/src/components/contacts/phone-number.ts b/src/components/contacts/phone-number.ts new file mode 100644 index 0000000000..9510a614c0 --- /dev/null +++ b/src/components/contacts/phone-number.ts @@ -0,0 +1,162 @@ +import {t} from '@lingui/macro' +import { + isSupportedCountry, + ParseError, + parsePhoneNumber, + parsePhoneNumberWithError, + type PhoneNumber, +} from 'libphonenumber-js/max' + +import {type CountryCode} from '#/lib/international-telephone-codes' + +/** + * Intended for after the user has finished inputting their phone number. + */ +export function processPhoneNumber( + number: string, + country: CountryCode, +): + | { + valid: true + formatted: string + countryCode: CountryCode + } + | { + valid: false + reason?: string + } { + try { + const phoneNumber = parsePhoneNumberWithError(number, { + defaultCountry: country, + }) + if (!phoneNumber.isValid()) { + return {valid: false, reason: t`Invalid phone number`} + } + const type = phoneNumber.getType() + if ( + type !== 'MOBILE' && + type !== 'FIXED_LINE_OR_MOBILE' && + type !== 'PERSONAL_NUMBER' + ) { + return { + valid: false, + reason: t`Number should be a mobile number`, + } + } + let countryCode = country + if (phoneNumber.country && phoneNumber.country !== country) { + if (phoneNumber.country === 'AC' || phoneNumber.country === 'TA') { + countryCode = 'SH' + } else { + countryCode = phoneNumber.country + } + } + return { + valid: true, + formatted: formatE164lWithoutCountryCode(phoneNumber), + countryCode, + } + } catch (error) { + if (error instanceof ParseError) { + return {valid: false, reason: error.message} + } else { + return {valid: false} + } + } +} + +/** + * Format a phone number as the international format with the prefix + * removed. + */ +function formatE164lWithoutCountryCode(phoneNumber: PhoneNumber) { + const intl = phoneNumber.format('E.164') + const prefix = '+' + phoneNumber.countryCallingCode + return intl.replace(prefix, '').trim() +} + +/** + * Takes a country code and a prefix-less phone number and constructs a full phone number. + * + * Does not have nice error handling - if you're unsure if the number is valid, use + * `processPhoneNumber` instead + */ +export function constructFullPhoneNumber( + countryCode: CountryCode, + phoneNumber: string, +) { + const result = parsePhoneNumber(phoneNumber, {defaultCountry: countryCode}) + if (!result.isValid()) + throw new Error('Invalid phone number passed to constructFullPhoneNumber') + return result.format('E.164') +} + +/** + * Takes a phone number and applies human-readable formatting. Do not sent to the API - they + * expect E.164 format. + */ +export function prettyPhoneNumber(phoneNumber: string) { + const result = parsePhoneNumber(phoneNumber) + return result.formatNational() +} + +/** + * Attempts to parse a phone number from a string, and returns the country code + * and the rest of the number if possible. If the number is invalid, returns undefined. + */ +export function getCountryCodeFromPastedNumber( + text: string, +): {countryCode: CountryCode; rest: string} | undefined { + try { + const phoneNumber = parsePhoneNumber(text) + if (!phoneNumber.isValid()) { + return undefined + } + const countryCode = phoneNumber.country + // we don't have AC and TA in our dropdown - see `#/lib/international-telephone-codes` + if (countryCode && countryCode !== 'AC' && countryCode !== 'TA') { + return { + countryCode, + rest: formatE164lWithoutCountryCode(phoneNumber), + } + } else { + return undefined + } + } catch (error) { + return undefined + } +} + +/** + * Normalizes a phone number into E.164 format + */ +export function normalizePhoneNumber( + rawNumber: string, + countryCode: string | undefined, + fallbackCountryCode: CountryCode, +): string | null { + try { + const result = parsePhoneNumber(rawNumber, { + defaultCountry: + countryCode && isSupportedCountry(countryCode) + ? countryCode + : fallbackCountryCode, + }) + + if (!result.isValid()) return null + + const type = result.getType() + if ( + type !== 'MOBILE' && + type !== 'FIXED_LINE_OR_MOBILE' && + type !== 'PERSONAL_NUMBER' + ) { + return null + } + + return result.format('E.164') + } catch (error) { + console.log('Failed to normalize phone number:', error) + return null + } +} diff --git a/src/components/contacts/screens/GetContacts.tsx b/src/components/contacts/screens/GetContacts.tsx new file mode 100644 index 0000000000..7057819e1f --- /dev/null +++ b/src/components/contacts/screens/GetContacts.tsx @@ -0,0 +1,290 @@ +import {Alert, View} from 'react-native' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import * as Contacts from 'expo-contacts' +import {AppBskyContactImportContacts} from '@atproto/api' +import {msg, t, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useMutation, useQueryClient} from '@tanstack/react-query' + +import {cleanError, isNetworkError} from '#/lib/strings/errors' +import {logger} from '#/logger' +import {findContactsStatusQueryKey} from '#/state/queries/find-contacts' +import {useAgent} from '#/state/session' +import {atoms as a, ios, tokens, useGutters} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import * as Layout from '#/components/Layout' +import {Loader} from '#/components/Loader' +import * as Toast from '#/components/Toast' +import {Text} from '#/components/Typography' +import { + contactsWithPhoneNumbersOnly, + filterMatchedNumbers, + getMatchedContacts, + normalizeContactBook, +} from '../contacts' +import {constructFullPhoneNumber} from '../phone-number' +import {type Action, type State} from '../state' + +const MAX_UPLOAD_COUNT = 1000 + +export function GetContacts({ + state, + dispatch, + onCancel, + context, +}: { + state: Extract + dispatch: React.ActionDispatch<[Action]> + onCancel: () => void + context: 'Onboarding' | 'Standalone' +}) { + const {_} = useLingui() + const agent = useAgent() + const insets = useSafeAreaInsets() + const gutters = useGutters([0, 'wide']) + const queryClient = useQueryClient() + + const {mutate: uploadContacts, isPending: isUploadPending} = useMutation({ + mutationFn: async (contacts: Contacts.ExistingContact[]) => { + const {phoneNumbers, indexToContactId} = normalizeContactBook( + contacts, + state.phoneCountryCode, + constructFullPhoneNumber(state.phoneCountryCode, state.phoneNumber), + ) + + if (phoneNumbers.length > 0) { + const res = await agent.app.bsky.contact.importContacts({ + token: state.token, + contacts: phoneNumbers.slice(0, MAX_UPLOAD_COUNT), + }) + + return { + matches: res.data.matchesAndContactIndexes, + indexToContactId, + } + } else { + return { + matches: [], + indexToContactId, + } + } + }, + onSuccess: (result, contacts) => { + if (context === 'Onboarding') { + logger.metric('onboarding:contacts:contactsShared', {}) + } + if (result.matches.length > 0) { + logger.metric('contacts:import:success', { + contactCount: contacts.length, + matchCount: result.matches.length, + entryPoint: context, + }) + } else { + logger.metric('contacts:import:failure', { + reason: 'noValidNumbers', + entryPoint: context, + }) + } + dispatch({ + type: 'SYNC_CONTACTS_SUCCESS', + payload: { + matches: getMatchedContacts( + contacts, + result.matches, + result.indexToContactId, + ), + contacts: filterMatchedNumbers( + contacts, + result.matches, + result.indexToContactId, + ), + }, + }) + queryClient.invalidateQueries({ + queryKey: findContactsStatusQueryKey, + }) + }, + onError: err => { + logger.metric('contacts:import:failure', { + reason: isNetworkError(err) ? 'networkError' : 'unknown', + entryPoint: context, + }) + if (isNetworkError(err)) { + Toast.show( + _( + msg`There was a problem with your internet connection, please try again`, + ), + {type: 'error'}, + ) + } else if ( + err instanceof AppBskyContactImportContacts.TooManyContactsError + ) { + Toast.show( + _( + msg`Too many contacts - you've exceeded the number of contacts you can import to find your friends`, + ), + {type: 'error'}, + ) + } else if ( + err instanceof AppBskyContactImportContacts.InvalidTokenError + ) { + Toast.show( + _( + msg`Could not upload contacts. You need to re-verify your phone number to proceed`, + ), + {type: 'error'}, + ) + } else { + logger.error('Error uploading contacts', {safeMessage: err}) + Toast.show(_(msg`Could not upload contacts. ${cleanError(err)}`), { + type: 'error', + }) + } + }, + }) + + const {mutate: getContacts, isPending: isGetContactsPending} = useMutation({ + mutationFn: async () => { + let permissions = await Contacts.getPermissionsAsync() + + if (!permissions.granted && permissions.canAskAgain) { + permissions = await Contacts.requestPermissionsAsync() + } + + logger.metric('contacts:permission:request', { + status: permissions.granted ? 'granted' : 'denied', + accessLevelIOS: ios(permissions.accessPrivileges), + }) + + if (!permissions.granted) { + throw new PermissionDeniedError() + } + + const contacts = await Contacts.getContactsAsync({ + fields: [ + Contacts.Fields.FirstName, + Contacts.Fields.LastName, + Contacts.Fields.PhoneNumbers, + Contacts.Fields.Image, + ], + }) + + return contactsWithPhoneNumbersOnly(contacts.data) + }, + onSuccess: contacts => { + dispatch({ + type: 'GET_CONTACTS_SUCCESS', + payload: {contacts}, + }) + uploadContacts(contacts) + }, + onError: err => { + if (err instanceof PermissionDeniedError) { + showPermissionDeniedAlert() + } else { + logger.error('Error getting contacts', {safeMessage: err}) + } + }, + }) + + const isPending = isUploadPending || isGetContactsPending + + const style = [a.text_md, a.leading_snug, a.mt_md] + + return ( + + + + Share your contacts to find friends + + + + Bluesky helps friends find each other by creating an encoded digital + fingerprint, called a "hash," and then looking for matching hashes. + + + + • We never store plain phone numbers + + + • We delete hashes after matches are made + + + • We only suggest follows if both people consent + + + • You can always opt out and delete your data + + + + We apply the highest privacy standards, and never share or sell your + contact information. + + + + + + + I consent to Bluesky using my contacts for mutual friend discovery + and to retain hashed data for matching until I opt out. + + + + + + + ) +} + +class PermissionDeniedError extends Error { + constructor() { + super('Permission denied') + } +} + +function showPermissionDeniedAlert() { + Alert.alert( + t`You've denied access to your contacts`, + t`You'll need to go to the System Settings for Bluesky and give permission if you want to use this feature.`, + [ + { + text: t`OK`, + style: 'default', + }, + ], + ) +} diff --git a/src/components/contacts/screens/PhoneInput.tsx b/src/components/contacts/screens/PhoneInput.tsx new file mode 100644 index 0000000000..0e08c09654 --- /dev/null +++ b/src/components/contacts/screens/PhoneInput.tsx @@ -0,0 +1,326 @@ +import {useState} from 'react' +import {Keyboard, View} from 'react-native' +import {KeyboardAvoidingView} from 'react-native-keyboard-controller' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {AppBskyContactStartPhoneVerification} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useMutation} from '@tanstack/react-query' + +import { + type CountryCode, + getDefaultCountry, +} from '#/lib/international-telephone-codes' +import {cleanError, isNetworkError} from '#/lib/strings/errors' +import {logger} from '#/logger' +import {useAgent} from '#/state/session' +import {OnboardingPosition} from '#/screens/Onboarding/Layout' +import { + android, + atoms as a, + platform, + tokens, + useGutters, + useTheme, +} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import * as TextField from '#/components/forms/TextField' +import {InternationalPhoneCodeSelect} from '#/components/InternationalPhoneCodeSelect' +import * as Layout from '#/components/Layout' +import {InlineLinkText} from '#/components/Link' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' +import {useGeolocation} from '#/geolocation' +import {isFindContactsFeatureEnabled} from '../country-allowlist' +import { + constructFullPhoneNumber, + getCountryCodeFromPastedNumber, + processPhoneNumber, +} from '../phone-number' +import {type Action, type State, useOnPressBackButton} from '../state' + +export function PhoneInput({ + state, + dispatch, + context, + onSkip, +}: { + state: Extract + dispatch: React.ActionDispatch<[Action]> + context: 'Onboarding' | 'Standalone' + onSkip: () => void +}) { + const {_} = useLingui() + const t = useTheme() + const agent = useAgent() + const location = useGeolocation() + const [countryCode, setCountryCode] = useState( + () => state.phoneCountryCode ?? getDefaultCountry(location), + ) + const [phoneNumber, setPhoneNumber] = useState(state.phoneNumber ?? '') + const gutters = useGutters([0, 'wide']) + const insets = useSafeAreaInsets() + // for API/generic errors + const [error, setError] = useState('') + // for issues with parsing the number + const [formatError, setFormatError] = useState('') + + const {mutate: submit, isPending} = useMutation({ + mutationFn: async ({ + phoneCountryCode, + phoneNumber, + }: { + phoneCountryCode: CountryCode + phoneNumber: string + }) => { + // sends a onetime code to the user's phone number + await agent.app.bsky.contact.startPhoneVerification({ + phone: constructFullPhoneNumber(phoneCountryCode, phoneNumber), + }) + }, + onSuccess: (_data, {phoneCountryCode, phoneNumber}) => { + dispatch({ + type: 'SUBMIT_PHONE_NUMBER', + payload: {phoneCountryCode, phoneNumber}, + }) + + logger.metric('contacts:phone:phoneEntered', {entryPoint: context}) + }, + onMutate: () => { + Keyboard.dismiss() + setError('') + setFormatError('') + }, + onError: err => { + if (isNetworkError(err)) { + setError( + _( + msg`A network error occurred. Please check your internet connection`, + ), + ) + } else if ( + err instanceof + AppBskyContactStartPhoneVerification.RateLimitExceededError + ) { + setError(_(msg`Rate limit exceeded. Please try again later.`)) + } else if ( + err instanceof AppBskyContactStartPhoneVerification.InvalidPhoneError + ) { + setError( + _( + msg`The verification provider was unable to send a code to your phone number. Please check your phone number and try again.`, + ), + ) + } else { + logger.error('Verify phone number failed', {safeMessage: err}) + setError(_(msg`An error occurred. ${cleanError(err)}`)) + } + }, + }) + + const isFeatureEnabled = isFindContactsFeatureEnabled(countryCode) + + const onSubmitNumber = () => { + if (!isFeatureEnabled) return + if (!phoneNumber) return + const result = processPhoneNumber(phoneNumber, countryCode) + if (result.valid) { + setPhoneNumber(result.formatted) + setCountryCode(result.countryCode) + + if (!isFindContactsFeatureEnabled(result.countryCode)) return + + submit({ + phoneCountryCode: result.countryCode, + phoneNumber: result.formatted, + }) + } else { + setFormatError(result.reason ?? _(msg`Invalid phone number`)) + } + } + + const paddingBottom = Math.max(insets.bottom, tokens.space.xl) + + const onPressBack = useOnPressBackButton() + + return ( + + + + + {context === 'Onboarding' ? ( + + ) : ( + + )} + + + {context === 'Onboarding' && } + + Verify phone number + + + + We need to verify your number before we can look for your friends. A + verification code will be sent to this number. + + + + + + Phone number + + + + setCountryCode(value)} + /> + + + + { + if (formatError) setFormatError('') + if (Math.abs(text.length - phoneNumber.length) > 1) { + // possibly pasted/autocompleted? auto-switch + // country code if possible + const result = getCountryCodeFromPastedNumber(text) + if (result) { + setCountryCode(result.countryCode) + setPhoneNumber(result.rest) + return + } + } + setPhoneNumber(text) + }} + placeholder={null} + keyboardType={platform({ + ios: 'number-pad', + android: 'phone-pad', + })} + autoComplete="tel" + returnKeyType={android('next')} + onSubmitEditing={onSubmitNumber} + /> + + + + + + {!isFeatureEnabled && ( + + + Support for this feature in your country has not been enabled yet! + Please check back later. + + + )} + {error && {error}} + {formatError && {formatError}} + + + + + + + + + + + + ) +} + +function LegalDisclaimer() { + const t = useTheme() + const {_} = useLingui() + + const style = [a.text_xs, t.atoms.text_contrast_medium, a.leading_snug] + + return ( + + + How we use your number: + + + •{' '} + Sent to our phone number verification provider Plivo + + + • Deleted by Plivo after verification + + + •{' '} + Held by Bluesky for 7 days to prevent abuse, then deleted + + + •{' '} + Stored as part of a secure code for matching with others + + + + By continuing, you consent to this use. You may change your mind any + time by visiting settings.{' '} + + Learn more + + + + + ) +} + +function ErrorText({children}: {children: React.ReactNode}) { + const t = useTheme() + return ( + + {children} + + ) +} diff --git a/src/components/contacts/screens/VerifyNumber.tsx b/src/components/contacts/screens/VerifyNumber.tsx new file mode 100644 index 0000000000..ec676ad724 --- /dev/null +++ b/src/components/contacts/screens/VerifyNumber.tsx @@ -0,0 +1,386 @@ +import {useEffect, useMemo, useState} from 'react' +import {Text as NestedText, View} from 'react-native' +import { + AppBskyContactStartPhoneVerification, + AppBskyContactVerifyPhone, +} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useMutation} from '@tanstack/react-query' + +import {clamp} from '#/lib/numbers' +import {cleanError, isNetworkError} from '#/lib/strings/errors' +import {logger} from '#/logger' +import {useAgent} from '#/state/session' +import {OnboardingPosition} from '#/screens/Onboarding/Layout' +import {atoms as a, useGutters, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as RetryIcon} from '#/components/icons/ArrowRotate' +import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheckIcon} from '#/components/icons/CircleCheck' +import {type Props as SVGIconProps} from '#/components/icons/common' +import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' +import * as Layout from '#/components/Layout' +import {Loader} from '#/components/Loader' +import * as Toast from '#/components/Toast' +import {Text} from '#/components/Typography' +import {OTPInput} from '../components/OTPInput' +import {constructFullPhoneNumber, prettyPhoneNumber} from '../phone-number' +import {type Action, type State, useOnPressBackButton} from '../state' + +export function VerifyNumber({ + state, + dispatch, + context, + onSkip, +}: { + state: Extract + dispatch: React.ActionDispatch<[Action]> + context: 'Onboarding' | 'Standalone' + onSkip: () => void +}) { + const t = useTheme() + const {_} = useLingui() + const agent = useAgent() + const gutters = useGutters([0, 'wide']) + + const [otpCode, setOtpCode] = useState('') + const [error, setError] = useState<{ + retryable: boolean + isResendError: boolean + message: string + } | null>(null) + + const [prevOtpCode, setPrevOtpCode] = useState(otpCode) + if (otpCode !== prevOtpCode) { + setPrevOtpCode(otpCode) + setError(null) + } + + const phone = useMemo( + () => constructFullPhoneNumber(state.phoneCountryCode, state.phoneNumber), + [state.phoneCountryCode, state.phoneNumber], + ) + + const prettyNumber = useMemo(() => prettyPhoneNumber(phone), [phone]) + + const { + mutate: verifyNumber, + isPending, + isSuccess, + } = useMutation({ + mutationFn: async (code: string) => { + const res = await agent.app.bsky.contact.verifyPhone({code, phone}) + return res.data.token + }, + onSuccess: async token => { + // let the success state show for a moment + setTimeout(() => { + dispatch({ + type: 'VERIFY_PHONE_NUMBER_SUCCESS', + payload: { + token, + }, + }) + }, 1000) + + logger.metric('contacts:phone:phoneVerified', {entryPoint: context}) + }, + onMutate: () => setError(null), + onError: err => { + setOtpCode('') + if (isNetworkError(err)) { + setError({ + retryable: true, + isResendError: false, + message: _( + msg`A network error occurred. Please check your internet connection.`, + ), + }) + } else if (err instanceof AppBskyContactVerifyPhone.InvalidCodeError) { + setError({ + retryable: true, + isResendError: true, + message: _(msg`This code is invalid. Resend to get a new code.`), + }) + } else if (err instanceof AppBskyContactVerifyPhone.InvalidPhoneError) { + setError({ + retryable: false, + isResendError: false, + message: _( + msg`The verification provider was unable to send a code to your phone number. Please check your phone number and try again.`, + ), + }) + } else if ( + err instanceof AppBskyContactVerifyPhone.RateLimitExceededError + ) { + setError({ + retryable: true, + isResendError: false, + message: _( + msg`Too many attempts. Please wait a few minutes and try again.`, + ), + }) + } else { + logger.error('Verify phone number failed', {safeMessage: err}) + setError({ + retryable: true, + isResendError: false, + message: _(msg`An error occurred. ${cleanError(err)}`), + }) + } + }, + }) + + const {mutate: resendCode, isPending: isResendingCode} = useMutation({ + mutationFn: async () => { + await agent.app.bsky.contact.startPhoneVerification({phone: phone}) + }, + onSuccess: () => { + dispatch({type: 'RESEND_VERIFICATION_CODE'}) + Toast.show(_(msg`A new code has been sent`)) + }, + onMutate: () => { + setOtpCode('') + setError(null) + }, + onError: err => { + if (isNetworkError(err)) { + setError({ + retryable: true, + isResendError: true, + message: _( + msg`A network error occurred. Please check your internet connection.`, + ), + }) + } else if ( + err instanceof AppBskyContactStartPhoneVerification.InvalidPhoneError + ) { + setError({ + retryable: false, + isResendError: true, + message: _( + msg`The verification provider was unable to send a code to your phone number. Please check your phone number and try again.`, + ), + }) + } else if ( + err instanceof + AppBskyContactStartPhoneVerification.RateLimitExceededError + ) { + setError({ + retryable: true, + isResendError: true, + message: _( + msg`Too many codes sent. Please wait a few minutes and try again.`, + ), + }) + } else { + logger.error('Resend failed', {safeMessage: err}) + setError({ + retryable: true, + isResendError: true, + message: _(msg`An error occurred. ${cleanError(err)}`), + }) + } + }, + }) + + const onPressBack = useOnPressBackButton() + + return ( + + + + + {context === 'Onboarding' ? ( + + ) : ( + + )} + + + {context === 'Onboarding' && } + + Verify phone number + + + Enter the 6-digit code sent to {prettyNumber} + + + verifyNumber(code)} + /> + + + resendCode()} + onRetry={() => verifyNumber(otpCode)} + lastCodeSentAt={state.lastSentAt} + /> + + + + ) +} + +/** + * Horrible component that takes all the state above and figures out what messages + * and buttons to display. + */ +function OTPStatus({ + error, + isPending, + isResendingCode, + isSuccess, + onResend, + onRetry, + lastCodeSentAt, +}: { + error: { + retryable: boolean + isResendError: boolean + message: string + } | null + isPending: boolean + isResendingCode: boolean + isSuccess: boolean + onResend: () => void + onRetry: () => void + lastCodeSentAt: Date | null +}) { + const {_} = useLingui() + const t = useTheme() + + const [time, setTime] = useState(Date.now()) + useEffect(() => { + const interval = setInterval(() => { + setTime(Date.now()) + }, 1000) + return () => clearInterval(interval) + }, []) + + const timeUntilCanResend = Math.max( + 0, + 30000 - (time - (lastCodeSentAt?.getTime() ?? 0)), + ) + const isWaiting = timeUntilCanResend > 0 + + let Icon: React.ComponentType | null = null + let text = '' + let textColor = t.atoms.text_contrast_medium.color + let showResendButton = false + let showRetryButton = false + + if (isSuccess) { + Icon = CircleCheckIcon + text = _(msg`Phone number verified`) + textColor = t.palette.positive_500 + } else if (isPending) { + text = _(msg`Verifying...`) + } else if (error) { + Icon = WarningIcon + text = error.message + textColor = t.palette.negative_500 + if (error.retryable) { + if (error.isResendError) { + showResendButton = true + } else { + showRetryButton = true + } + } + } else { + showResendButton = true + } + + return ( + + {text && ( + + {Icon && } + + {text} + + + )} + + {showRetryButton && ( + + )} + + {showResendButton && ( + + )} + + ) +} diff --git a/src/components/contacts/screens/ViewMatches.tsx b/src/components/contacts/screens/ViewMatches.tsx new file mode 100644 index 0000000000..9b4ff9382c --- /dev/null +++ b/src/components/contacts/screens/ViewMatches.tsx @@ -0,0 +1,683 @@ +import {useCallback, useMemo, useRef, useState} from 'react' +import {View} from 'react-native' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import * as SMS from 'expo-sms' +import {type ModerationOpts} from '@atproto/api' +import {msg, Plural, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useMutation, useQueryClient} from '@tanstack/react-query' + +import {wait} from '#/lib/async/wait' +import {cleanError, isNetworkError} from '#/lib/strings/errors' +import {logger} from '#/logger' +import { + updateProfileShadow, + useProfileShadow, +} from '#/state/cache/profile-shadow' +import {useModerationOpts} from '#/state/preferences/moderation-opts' +import { + optimisticRemoveMatch, + useMatchesPassthroughQuery, +} from '#/state/queries/find-contacts' +import {useAgent, useSession} from '#/state/session' +import {List, type ListMethods} from '#/view/com/util/List' +import {UserAvatar} from '#/view/com/util/UserAvatar' +import {OnboardingPosition} from '#/screens/Onboarding/Layout' +import {bulkWriteFollows} from '#/screens/Onboarding/util' +import {atoms as a, tokens, useGutters, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {SearchInput} from '#/components/forms/SearchInput' +import {useInteractionState} from '#/components/hooks/useInteractionState' +import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check' +import {MagnifyingGlassX_Stroke2_Corner0_Rounded_Large as SearchFailedIcon} from '#/components/icons/MagnifyingGlass' +import {PersonX_Stroke2_Corner0_Rounded_Large as PersonXIcon} from '#/components/icons/Person' +import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus' +import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' +import * as Layout from '#/components/Layout' +import {ListFooter} from '#/components/Lists' +import {Loader} from '#/components/Loader' +import * as ProfileCard from '#/components/ProfileCard' +import * as Toast from '#/components/Toast' +import {Text} from '#/components/Typography' +import type * as bsky from '#/types/bsky' +import {InviteInfo} from '../components/InviteInfo' +import {type Action, type Contact, type Match, type State} from '../state' + +type Item = + | { + type: 'matches header' + count: number + } + | { + type: 'match' + match: Match + } + | { + type: 'contacts header' + } + | { + type: 'contact' + contact: Contact + } + | { + type: 'no matches header' + } + | { + type: 'search empty state' + query: string + } + | { + type: 'totally empty state' + } + +export function ViewMatches({ + state, + dispatch, + context, + onNext, +}: { + state: Extract + dispatch: React.ActionDispatch<[Action]> + context: 'Onboarding' | 'Standalone' + onNext: () => void +}) { + const t = useTheme() + const {_} = useLingui() + const gutter = useGutters([0, 'wide']) + const moderationOpts = useModerationOpts() + const queryClient = useQueryClient() + const agent = useAgent() + const insets = useSafeAreaInsets() + const listRef = useRef(null) + + const [search, setSearch] = useState('') + const { + state: searchFocused, + onIn: onFocus, + onOut: onBlur, + } = useInteractionState() + + // HACK: Although we already have the match data, we need to pass it through + // a query to get it into the shadow state + const allMatches = useMatchesPassthroughQuery(state.matches) + const matches = allMatches.filter( + match => !state.dismissedMatches.includes(match.profile.did), + ) + + console.log(matches) + + const followableDids = matches.map(match => match.profile.did) + const [didFollowAll, setDidFollowAll] = useState(followableDids.length === 0) + + const cumulativeFollowCount = useRef(0) + const onFollow = useCallback(() => { + logger.metric('contacts:matches:follow', {entryPoint: context}) + cumulativeFollowCount.current += 1 + }, [context]) + + const {mutate: followAll, isPending: isFollowingAll} = useMutation({ + mutationFn: async () => { + for (const did of followableDids) { + updateProfileShadow(queryClient, did, { + followingUri: 'pending', + }) + } + + const uris = await wait(500, bulkWriteFollows(agent, followableDids)) + + for (const did of followableDids) { + const uri = uris.get(did) + updateProfileShadow(queryClient, did, { + followingUri: uri, + }) + } + return followableDids + }, + onMutate: () => + logger.metric('contacts:matches:followAll', { + followCount: followableDids.length, + entryPoint: context, + }), + onSuccess: () => { + setDidFollowAll(true) + Toast.show(_(msg`All friends followed!`), {type: 'success'}) + cumulativeFollowCount.current += followableDids.length + }, + onError: _err => { + Toast.show(_(msg`Failed to follow all your friends, please try again`), { + type: 'error', + }) + for (const did of followableDids) { + updateProfileShadow(queryClient, did, { + followingUri: undefined, + }) + } + }, + }) + + const items = useMemo(() => { + const all: Item[] = [] + + if (searchFocused || search.length > 0) { + for (const match of matches) { + if ( + search.length === 0 || + (match.profile.displayName ?? '') + .toLocaleLowerCase() + .includes(search.toLocaleLowerCase()) || + match.profile.handle + .toLocaleLowerCase() + .includes(search.toLocaleLowerCase()) + ) { + all.push({type: 'match', match}) + } + } + + for (const contact of state.contacts) { + if ( + search.length === 0 || + [contact.firstName, contact.lastName] + .filter(Boolean) + .join(' ') + .toLocaleLowerCase() + .includes(search.toLocaleLowerCase()) + ) { + all.push({type: 'contact', contact}) + } + } + + if (all.length === 0) { + all.push({type: 'search empty state', query: search}) + } + } else { + if (matches.length > 0) { + all.push({type: 'matches header', count: matches.length}) + for (const match of matches) { + all.push({type: 'match', match}) + } + + if (state.contacts.length > 0) { + all.push({type: 'contacts header'}) + } + } else if (state.contacts.length > 0) { + all.push({type: 'no matches header'}) + } + + for (const contact of state.contacts) { + all.push({type: 'contact', contact}) + } + + if (all.length === 0) { + all.push({type: 'totally empty state'}) + } + } + + return all + }, [matches, state.contacts, search, searchFocused]) + + const {mutate: dismissMatch} = useMutation({ + mutationFn: async (did: string) => { + await agent.app.bsky.contact.dismissMatch({subject: did}) + }, + onMutate: did => { + logger.metric('contacts:matches:dismiss', {entryPoint: context}) + dispatch({type: 'DISMISS_MATCH', payload: {did}}) + }, + onSuccess: (_res, did) => { + // for the other screen + optimisticRemoveMatch(queryClient, did) + }, + onError: (err, did) => { + dispatch({type: 'DISMISS_MATCH_FAILED', payload: {did}}) + if (isNetworkError(err)) { + Toast.show( + _( + msg`Failed to hide suggestion, please check your internet connection`, + ), + {type: 'error'}, + ) + } else { + logger.error('Dismissing match failed', {safeMessage: err}) + Toast.show( + _(msg`An error occurred while hiding suggestion. ${cleanError(err)}`), + {type: 'error'}, + ) + } + }, + }) + + const renderItem = ({item}: {item: Item}) => { + switch (item.type) { + case 'match': + return ( + + ) + case 'contact': + return + case 'matches header': + return ( +
      + }> + {item.count > 1 && ( + + )} +
      + ) + case 'contacts header': + return ( +
      + Invite friends{' '} + + + } + hasContentAbove + /> + ) + case 'no matches header': + return ( +
      + Bluesky is more fun with friends. Do you want to invite some of + yours?{' '} + + + } + /> + ) + case 'search empty state': + return + case 'totally empty state': + return + } + } + + const isSearchEmpty = items?.[0]?.type === 'search empty state' + const isTotallyEmpty = items?.[0]?.type === 'totally empty state' + + const isEmpty = isSearchEmpty || isTotallyEmpty + + return ( + + {context === 'Standalone' && ( + + + + + + )} + {!isTotallyEmpty && ( + + {context === 'Onboarding' && } + { + onFocus() + listRef.current?.scrollToOffset({offset: 0, animated: false}) + }} + onBlur={() => { + onBlur() + listRef.current?.scrollToOffset({offset: 0, animated: false}) + }} + onChangeText={text => { + setSearch(text) + listRef.current?.scrollToOffset({offset: 0, animated: false}) + }} + onClearText={() => setSearch('')} + /> + + )} + : null} + keyExtractor={keyExtractor} + keyboardDismissMode="interactive" + automaticallyAdjustKeyboardInsets + /> + + + + + ) +} + +function keyExtractor(item: Item) { + switch (item.type) { + case 'contact': + return item.contact.id + case 'match': + return item.match.profile.did + default: + return item.type + } +} + +function MatchItem({ + profile, + contact, + moderationOpts, + onRemoveSuggestion, + onFollow, +}: { + profile: bsky.profile.AnyProfileView + contact?: Contact + moderationOpts?: ModerationOpts + onRemoveSuggestion: (did: string) => void + onFollow: () => void +}) { + const gutter = useGutters([0, 'wide']) + const t = useTheme() + const {_} = useLingui() + const shadow = useProfileShadow(profile) + + const contactName = useMemo(() => { + if (!contact) return null + + const name = contact.firstName ?? contact.lastName ?? contact.name + if (name) return _(msg`Your contact ${name}`) + const phone = + contact.phoneNumbers?.find(p => p.isPrimary) ?? contact.phoneNumbers?.[0] + if (phone?.number) return phone.number + return null + }, [contact, _]) + + if (!moderationOpts) return null + + return ( + + + + + + + {contactName && ( + + {contactName} + + )} + + + {!shadow.viewer?.following && ( + + )} + + + ) +} + +function ContactItem({ + contact, + context, +}: { + contact: Contact + context: 'Onboarding' | 'Standalone' +}) { + const gutter = useGutters([0, 'wide']) + const t = useTheme() + const {_} = useLingui() + const {currentAccount} = useSession() + + const name = contact.firstName ?? contact.lastName ?? contact.name + const phone = + contact.phoneNumbers?.find(phone => phone.isPrimary) ?? + contact.phoneNumbers?.[0] + const phoneNumber = phone?.number + + return ( + + + {contact.image ? ( + + ) : ( + + + {name?.[0]?.toLocaleUpperCase()} + + + )} + + {name ?? No name} + + {phoneNumber && currentAccount && ( + + )} + + + ) +} + +function Header({ + titleText, + largeTitle, + subtitleText, + children, + hasContentAbove, +}: { + titleText: React.ReactNode + largeTitle?: boolean + subtitleText?: React.ReactNode + children?: React.ReactNode + hasContentAbove?: boolean +}) { + const gutter = useGutters([0, 'wide']) + const t = useTheme() + + return ( + + + + {titleText} + + {children} + + {subtitleText && ( + + {subtitleText} + + )} + + ) +} + +function SearchEmptyState({query}: {query: string}) { + const t = useTheme() + + return ( + + + + No contacts with the name “{query}” found + + + ) +} + +function TotallyEmptyState() { + const t = useTheme() + + return ( + + + + No contacts found + + + ) +} diff --git a/src/components/contacts/state.ts b/src/components/contacts/state.ts new file mode 100644 index 0000000000..2514a6a659 --- /dev/null +++ b/src/components/contacts/state.ts @@ -0,0 +1,198 @@ +import {createContext, useContext, useReducer} from 'react' +import {type GestureResponderEvent} from 'react-native' +import {type ExistingContact} from 'expo-contacts' + +import {type CountryCode} from '#/lib/international-telephone-codes' +import type * as bsky from '#/types/bsky' + +export type Contact = ExistingContact + +export type Match = { + profile: bsky.profile.AnyProfileView + contact?: Contact +} + +export type State = + | { + step: '1: phone input' + phoneCountryCode?: CountryCode + phoneNumber?: string + } + | { + step: '2: verify number' + phoneCountryCode: CountryCode + phoneNumber: string + lastSentAt: Date | null + } + | { + step: '3: get contacts' + phoneCountryCode: CountryCode + phoneNumber: string + token: string + contacts?: Contact[] + } + | { + step: '4: view matches' + contacts: Contact[] + matches: Match[] + // rather than mutating `matches`, we keep track of dismissed matches + // so we can roll back optimistic updates + dismissedMatches: string[] + } + +export type Action = + | { + type: 'SUBMIT_PHONE_NUMBER' + payload: { + phoneCountryCode: CountryCode + phoneNumber: string + } + } + | { + type: 'RESEND_VERIFICATION_CODE' + } + | { + type: 'VERIFY_PHONE_NUMBER_SUCCESS' + payload: { + token: string + } + } + | { + type: 'GET_CONTACTS_SUCCESS' + payload: { + contacts: Contact[] + } + } + | { + type: 'SYNC_CONTACTS_SUCCESS' + payload: { + matches: Match[] + // filter out matched contacts + contacts: Contact[] + } + } + | { + type: 'BACK' + } + | { + type: 'DISMISS_MATCH' + payload: { + did: string + } + } + | { + type: 'DISMISS_MATCH_FAILED' + payload: { + did: string + } + } + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'SUBMIT_PHONE_NUMBER': { + assertCurrentStep(state, '1: phone input') + return { + step: '2: verify number', + ...action.payload, + lastSentAt: null, + } + } + case 'RESEND_VERIFICATION_CODE': { + assertCurrentStep(state, '2: verify number') + return { + ...state, + lastSentAt: new Date(), + } + } + case 'VERIFY_PHONE_NUMBER_SUCCESS': { + assertCurrentStep(state, '2: verify number') + return { + step: '3: get contacts', + token: action.payload.token, + phoneCountryCode: state.phoneCountryCode, + phoneNumber: state.phoneNumber, + } + } + case 'BACK': { + assertCurrentStep(state, '2: verify number') + return { + step: '1: phone input', + phoneNumber: state.phoneNumber, + phoneCountryCode: state.phoneCountryCode, + } + } + case 'GET_CONTACTS_SUCCESS': { + assertCurrentStep(state, '3: get contacts') + return { + ...state, + contacts: action.payload.contacts, + } + } + case 'SYNC_CONTACTS_SUCCESS': { + assertCurrentStep(state, '3: get contacts') + return { + step: '4: view matches', + contacts: action.payload.contacts, + matches: action.payload.matches, + dismissedMatches: [], + } + } + case 'DISMISS_MATCH': { + assertCurrentStep(state, '4: view matches') + return { + ...state, + dismissedMatches: [ + ...new Set(state.dismissedMatches), + action.payload.did, + ], + } + } + case 'DISMISS_MATCH_FAILED': { + assertCurrentStep(state, '4: view matches') + return { + ...state, + dismissedMatches: state.dismissedMatches.filter( + did => did !== action.payload.did, + ), + } + } + } +} + +class InvalidStateTransitionError extends Error { + constructor(message: string) { + super(message) + this.name = 'InvalidStateTransitionError' + } +} + +function assertCurrentStep( + state: State, + step: S, +): asserts state is Extract { + if (state.step !== step) { + throw new InvalidStateTransitionError( + `Invalid state transition: expecting ${step}, got ${state.step}`, + ) + } +} + +export function useFindContactsFlowState( + initialState: State = {step: '1: phone input'}, +) { + return useReducer(reducer, initialState) +} + +export const FindContactsGoBackContext = createContext< + (() => void) | undefined +>(undefined) +export function useOnPressBackButton() { + const goBack = useContext(FindContactsGoBackContext) + if (!goBack) { + return undefined + } + return (evt: GestureResponderEvent) => { + evt.preventDefault() + goBack() + } +} diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx index 536483b080..58d669f0e2 100644 --- a/src/components/dialogs/GifSelect.tsx +++ b/src/components/dialogs/GifSelect.tsx @@ -29,7 +29,7 @@ import * as Dialog from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' import {useThrottledValue} from '#/components/hooks/useThrottledValue' import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow' -import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2' +import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass' import {ListFooter, ListMaybePlaceholder} from '#/components/Lists' export function GifSelectDialog({ diff --git a/src/components/dialogs/SearchablePeopleList.tsx b/src/components/dialogs/SearchablePeopleList.tsx index 41b31b255c..d8c44c3097 100644 --- a/src/components/dialogs/SearchablePeopleList.tsx +++ b/src/components/dialogs/SearchablePeopleList.tsx @@ -25,7 +25,7 @@ import {Button, ButtonIcon} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {canBeMessaged} from '#/components/dms/util' import {useInteractionState} from '#/components/hooks/useInteractionState' -import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2' +import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import * as ProfileCard from '#/components/ProfileCard' import {Text} from '#/components/Typography' diff --git a/src/components/dialogs/nuxs/FindContactsAnnouncement.tsx b/src/components/dialogs/nuxs/FindContactsAnnouncement.tsx new file mode 100644 index 0000000000..13f62a3e20 --- /dev/null +++ b/src/components/dialogs/nuxs/FindContactsAnnouncement.tsx @@ -0,0 +1,116 @@ +import {useCallback} from 'react' +import {View} from 'react-native' +import {Image} from 'expo-image' +import {LinearGradient} from 'expo-linear-gradient' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {logger} from '#/logger' +import {isWeb} from '#/platform/detection' +import {atoms as a, useTheme, web} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import {useNuxDialogContext} from '#/components/dialogs/nuxs' +import {Text} from '#/components/Typography' +import {navigate} from '#/Navigation' + +export function FindContactsAnnouncement() { + const t = useTheme() + const {_} = useLingui() + const nuxDialogs = useNuxDialogContext() + const control = Dialog.useDialogControl() + + Dialog.useAutoOpen(control) + + const onClose = useCallback(() => { + nuxDialogs.dismissActiveNux() + }, [nuxDialogs]) + + return ( + + + + + + + + {_( + + + + + + Find your friends + + + + Bluesky is more fun with friends! Import your contacts to see + who’s already here. + + + + + + + + + + + ) +} diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx index 4076708fc0..63e11a7f4b 100644 --- a/src/components/dialogs/nuxs/index.tsx +++ b/src/components/dialogs/nuxs/index.tsx @@ -1,8 +1,16 @@ -import React from 'react' +import { + createContext, + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from 'react' import {type AppBskyActorDefs} from '@atproto/api' import {useGate} from '#/lib/statsig/statsig' import {logger} from '#/logger' +import {isNative} from '#/platform/detection' import {STALE} from '#/state/queries' import {Nux, useNuxs, useResetNuxs, useSaveNux} from '#/state/queries/nuxs' import { @@ -12,11 +20,11 @@ import { import {useProfileQuery} from '#/state/queries/profile' import {type SessionAccount, useSession} from '#/state/session' import {useOnboardingState} from '#/state/shell' -import {BookmarksAnnouncement} from '#/components/dialogs/nuxs/BookmarksAnnouncement' +import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing' /* * NUXs */ -import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing' +import {FindContactsAnnouncement} from './FindContactsAnnouncement' import {isExistingUserAsOf} from './utils' type Context = { @@ -34,24 +42,24 @@ const queuedNuxs: { }) => boolean }[] = [ { - id: Nux.BookmarksAnnouncement, + id: Nux.FindContactsAnnouncement, enabled: ({currentProfile}) => { - return isExistingUserAsOf( - '2025-09-08T00:00:00.000Z', - currentProfile.createdAt, + return ( + isNative && + isExistingUserAsOf('2025-12-16T00:00:00.000Z', currentProfile.createdAt) ) }, }, ] -const Context = React.createContext({ +const Context = createContext({ activeNux: undefined, dismissActiveNux: () => {}, }) Context.displayName = 'NuxDialogContext' export function useNuxDialogContext() { - return React.useContext(Context) + return useContext(Context) } export function NuxDialogs() { @@ -92,19 +100,19 @@ function Inner({ }) { const gate = useGate() const {nuxs} = useNuxs() - const [snoozed, setSnoozed] = React.useState(() => { + const [snoozed, setSnoozed] = useState(() => { return isSnoozed() }) - const [activeNux, setActiveNux] = React.useState() + const [activeNux, setActiveNux] = useState() const {mutateAsync: saveNux} = useSaveNux() const {mutate: resetNuxs} = useResetNuxs() - const snoozeNuxDialog = React.useCallback(() => { + const snoozeNuxDialog = useCallback(() => { snooze() setSnoozed(true) }, [setSnoozed]) - const dismissActiveNux = React.useCallback(() => { + const dismissActiveNux = useCallback(() => { if (!activeNux) return setActiveNux(undefined) }, [activeNux, setActiveNux]) @@ -118,7 +126,7 @@ function Inner({ } } - React.useEffect(() => { + useEffect(() => { if (snoozed) return // comment this out to test if (!nuxs) return @@ -170,7 +178,7 @@ function Inner({ preferences, ]) - const ctx = React.useMemo(() => { + const ctx = useMemo(() => { return { activeNux, dismissActiveNux, @@ -180,7 +188,9 @@ function Inner({ return ( {/*For example, activeNux === Nux.NeueTypography && */} - {activeNux === Nux.BookmarksAnnouncement && } + {activeNux === Nux.FindContactsAnnouncement && ( + + )} ) } diff --git a/src/components/forms/SearchInput.tsx b/src/components/forms/SearchInput.tsx index 618a0040ef..ae0335abd4 100644 --- a/src/components/forms/SearchInput.tsx +++ b/src/components/forms/SearchInput.tsx @@ -8,7 +8,7 @@ import {isNative} from '#/platform/detection' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' import * as TextField from '#/components/forms/TextField' -import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlassIcon} from '#/components/icons/MagnifyingGlass2' +import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlassIcon} from '#/components/icons/MagnifyingGlass' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' type SearchInputProps = Omit & { diff --git a/src/components/forms/TextField.tsx b/src/components/forms/TextField.tsx index 62f30db47b..6e8846ddd6 100644 --- a/src/components/forms/TextField.tsx +++ b/src/components/forms/TextField.tsx @@ -141,17 +141,28 @@ export function useSharedInputStyles() { }, [t]) } -export type InputProps = Omit & { +export type InputProps = Omit< + TextInputProps, + 'value' | 'onChangeText' | 'placeholder' +> & { label: string /** * @deprecated Controlled inputs are *strongly* discouraged. Use `defaultValue` instead where possible. * * See https://github.com/facebook/react-native-website/pull/4247 + * + * Note: This guidance no longer applies once we migrate to the New Architecture! */ value?: string onChangeText?: (value: string) => void isInvalid?: boolean inputRef?: React.RefObject | React.ForwardedRef + /** + * Note: this currently falls back to the label if not specified. However, + * most new designs have no placeholder. We should eventually remove this fallback + * behaviour, but for now just pass `null` if you want no placeholder -sfn + */ + placeholder?: string | null | undefined } export function createInput(Component: typeof TextInput) { @@ -255,7 +266,7 @@ export function createInput(Component: typeof TextInput) { ctx.onBlur() onBlur?.(e) }} - placeholder={placeholder || label} + placeholder={placeholder === null ? undefined : placeholder || label} placeholderTextColor={t.palette.contrast_500} keyboardAppearance={t.name === 'light' ? 'light' : 'dark'} style={flattened} @@ -292,12 +303,7 @@ export function LabelText({ return ( + style={[a.text_sm, a.font_medium, t.atoms.text_contrast_medium, a.mb_sm]}> {children} ) diff --git a/src/components/icons/ArrowRotateCounterClockwise.tsx b/src/components/icons/ArrowRotate.tsx similarity index 52% rename from src/components/icons/ArrowRotateCounterClockwise.tsx rename to src/components/icons/ArrowRotate.tsx index 35cd23a975..290b423135 100644 --- a/src/components/icons/ArrowRotateCounterClockwise.tsx +++ b/src/components/icons/ArrowRotate.tsx @@ -4,3 +4,9 @@ export const ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded = createSinglePathSVG({ path: 'M5 3a1 1 0 0 1 1 1v1.423c.498-.46 1.02-.869 1.58-1.213C8.863 3.423 10.302 3 12.028 3a9 9 0 1 1-8.487 12 1 1 0 0 1 1.885-.667A7 7 0 1 0 12.028 5c-1.37 0-2.444.327-3.402.915-.474.29-.93.652-1.383 1.085H9a1 1 0 0 1 0 2H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z', }) + +export const ArrowRotateClockwise_Stroke2_Corner0_Rounded = createSinglePathSVG( + { + path: 'M2.972 12a9 9 0 0 1 9-9c1.726 0 3.165.423 4.448 1.21.561.344 1.082.756 1.58 1.218V4a1 1 0 1 1 2 0v4a1 1 0 0 1-1 1h-4a1 1 0 1 1 0-2h1.756a8.3 8.3 0 0 0-1.382-1.085C14.417 5.327 13.341 5 11.972 5a7 7 0 1 0 6.601 9.333A1 1 0 0 1 20.46 15a9 9 0 0 1-17.487-3Z', + }, +) diff --git a/src/components/icons/Contacts.tsx b/src/components/icons/Contacts.tsx new file mode 100644 index 0000000000..93a08e30d9 --- /dev/null +++ b/src/components/icons/Contacts.tsx @@ -0,0 +1,9 @@ +import {createSinglePathSVG} from './TEMPLATE' + +export const Contacts_Stroke2_Corner2_Rounded = createSinglePathSVG({ + path: 'M7 4a1 1 0 0 0-1 1v12.05q.243-.05.5-.05H18V4H7Zm5 7c.894 0 1.57.188 2.068.513.445.29.683.647.806.937l.046.12c.285.836-.43 1.43-1.03 1.43h-3.78c-.6 0-1.315-.594-1.03-1.43l.046-.12c.123-.29.361-.647.806-.937C10.43 11.188 11.106 11 12 11Zm0-4a1.75 1.75 0 1 1 0 3.5A1.75 1.75 0 0 1 12 7Zm8 11a1 1 0 0 1-1 1H6.5a.5.5 0 0 0 0 1H19a1 1 0 1 1 0 2H6.5A2.5 2.5 0 0 1 4 19.5V5a3 3 0 0 1 3-3h11a2 2 0 0 1 2 2v14Z', +}) + +export const Contacts_Filled_Corner2_Rounded = createSinglePathSVG({ + path: 'M7 2a3 3 0 0 0-3 3v14.5A2.5 2.5 0 0 0 6.5 22H19a1 1 0 1 0 0-2H6.5a.5.5 0 0 1 0-1H19a1 1 0 0 0 1-1V4a2 2 0 0 0-2-2H7Zm5 5a1.75 1.75 0 1 0 0 3.5A1.75 1.75 0 0 0 12 7Zm0 4c-.894 0-1.57.188-2.068.512a2.07 2.07 0 0 0-.852 1.058c-.286.838.432 1.43 1.03 1.43h3.78c.598 0 1.316-.592 1.03-1.43a2.07 2.07 0 0 0-.852-1.058C13.57 11.189 12.894 11 12 11Z', +}) diff --git a/src/components/icons/MagnifyingGlass.tsx b/src/components/icons/MagnifyingGlass.tsx index de5f83c9cb..b10b326276 100644 --- a/src/components/icons/MagnifyingGlass.tsx +++ b/src/components/icons/MagnifyingGlass.tsx @@ -1,6 +1,20 @@ import {createSinglePathSVG} from './TEMPLATE' +export const MagnifyingGlass_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M11 5a6 6 0 1 0 0 12 6 6 0 0 0 0-12Zm-8 6a8 8 0 1 1 14.32 4.906l3.387 3.387a1 1 0 0 1-1.414 1.414l-3.387-3.387A8 8 0 0 1 3 11Z', +}) + export const MagnifyingGlass_Filled_Stroke2_Corner0_Rounded = createSinglePathSVG({ path: 'M5 11a6 6 0 1 1 12 0 6 6 0 0 1-12 0Zm6-8a8 8 0 1 0 4.906 14.32l3.387 3.387a1 1 0 0 0 1.414-1.414l-3.387-3.387A8 8 0 0 0 11 3Zm4 8a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z', }) + +export const MagnifyingGlassX_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M20 12a8 8 0 1 0-8 8c2.204 0 4.2-.89 5.648-2.333A7.97 7.97 0 0 0 20 12Zm-5.707-3.707a1 1 0 1 1 1.414 1.414L13.414 12l2.293 2.293a1 1 0 1 1-1.414 1.414L12 13.414l-2.293 2.293a1 1 0 1 1-1.414-1.414L10.586 12 8.293 9.707a1 1 0 1 1 1.414-1.414L12 10.586l2.293-2.293ZM22 12a9.96 9.96 0 0 1-2.269 6.34l2.891 2.89a1 1 0 1 1-1.414 1.415l-2.893-2.893A9.96 9.96 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10Z', +}) + +export const MagnifyingGlassX_Stroke2_Corner0_Rounded_Large = + createSinglePathSVG({ + viewBox: '0 0 64 64', + path: 'M55 32C55 19.298 44.703 9 32 9S9 19.298 9 32s10.298 23 23 23a22.93 22.93 0 0 0 16.235-6.708A22.93 22.93 0 0 0 55 32Zm-15.707-8.707a1 1 0 1 1 1.414 1.414L33.414 32l7.293 7.293a1 1 0 1 1-1.414 1.414L32 33.414l-7.293 7.293a1 1 0 1 1-1.414-1.414L30.586 32l-7.293-7.293a1 1 0 1 1 1.414-1.414L32 30.586l7.293-7.293ZM57 32a24.9 24.9 0 0 1-6.66 16.985l8.808 8.808a1 1 0 0 1-1.414 1.414l-8.81-8.81A24.9 24.9 0 0 1 32 57C18.193 57 7 45.807 7 32S18.193 7 32 7s25 11.193 25 25Z', + }) diff --git a/src/components/icons/MagnifyingGlass2.tsx b/src/components/icons/MagnifyingGlass2.tsx deleted file mode 100644 index 3ca4034001..0000000000 --- a/src/components/icons/MagnifyingGlass2.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import {createSinglePathSVG} from './TEMPLATE' - -export const MagnifyingGlass2_Stroke2_Corner0_Rounded = createSinglePathSVG({ - path: 'M11 5a6 6 0 1 0 0 12 6 6 0 0 0 0-12Zm-8 6a8 8 0 1 1 14.32 4.906l3.387 3.387a1 1 0 0 1-1.414 1.414l-3.387-3.387A8 8 0 0 1 3 11Z', -}) diff --git a/src/components/icons/Person.tsx b/src/components/icons/Person.tsx index 8428fcce1b..55324f10d6 100644 --- a/src/components/icons/Person.tsx +++ b/src/components/icons/Person.tsx @@ -16,6 +16,11 @@ export const PersonX_Stroke2_Corner0_Rounded = createSinglePathSVG({ path: 'M12 4a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5ZM7.5 6.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM5.679 19c.709-2.902 3.079-5 6.321-5 .302 0 .595.018.878.053a1 1 0 0 0 .243-1.985A9.235 9.235 0 0 0 12 12c-4.3 0-7.447 2.884-8.304 6.696-.29 1.29.767 2.304 1.902 2.304H12a1 1 0 1 0 0-2H5.679Zm9.614-3.707a1 1 0 0 1 1.414 0L18 16.586l1.293-1.293a1 1 0 0 1 1.414 1.414L19.414 18l1.293 1.293a1 1 0 0 1-1.414 1.414L18 19.414l-1.293 1.293a1 1 0 0 1-1.414-1.414L16.586 18l-1.293-1.293a1 1 0 0 1 0-1.414Z', }) +export const PersonX_Stroke2_Corner0_Rounded_Large = createSinglePathSVG({ + viewBox: '0 0 64 64', + path: 'M24.952 33.435c10.897 0 19.195 6.496 22.58 15.65.784 2.117.258 4.183-1 5.683-1.242 1.48-3.194 2.414-5.33 2.415h-32.5c-2.136 0-4.089-.935-5.331-2.415-1.258-1.5-1.783-3.566-1-5.682 3.386-9.155 11.683-15.651 22.58-15.651Zm0 2.298c-9.885 0-17.355 5.849-20.425 14.15-.47 1.271-.174 2.48.604 3.407.795.947 2.096 1.594 3.57 1.594h32.5c1.476 0 2.777-.647 3.571-1.594.778-.928 1.074-2.136.605-3.406-3.07-8.302-10.54-14.15-20.425-14.15Zm33.262-14.59a1.15 1.15 0 1 1 1.625 1.626l-5.688 5.687 5.686 5.688a1.15 1.15 0 1 1-1.625 1.625l-5.686-5.688-5.687 5.688a1.15 1.15 0 1 1-1.625-1.625l5.687-5.688-5.689-5.687a1.15 1.15 0 1 1 1.625-1.625l5.689 5.687 5.688-5.687ZM24.949 5.552c6.557 0 11.874 5.316 11.874 11.874 0 6.557-5.317 11.874-11.874 11.874s-11.874-5.317-11.874-11.874S18.39 5.55 24.949 5.55Zm0 2.299a9.575 9.575 0 0 0-9.575 9.575A9.575 9.575 0 0 0 24.949 27a9.575 9.575 0 0 0 9.575-9.575A9.575 9.575 0 0 0 24.95 7.85Z', +}) + export const PersonPlus_Stroke2_Corner0_Rounded = createSinglePathSVG({ path: 'M12 4a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5ZM7.5 6.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM5.678 19c.71-2.909 3.092-5 6.322-5 .621 0 1.206.077 1.748.218a1 1 0 1 0 .504-1.936A8.931 8.931 0 0 0 12 12c-4.758 0-8.083 3.521-8.496 7.906A1 1 0 0 0 4.5 21H11a1 1 0 1 0 0-2H5.678ZM18 14a1 1 0 0 1 1 1v2h2a1 1 0 1 1 0 2h-2v2a1 1 0 1 1-2 0v-2h-2a1 1 0 1 1 0-2h2v-2a1 1 0 0 1 1-1Z', }) diff --git a/src/components/intents/VerifyEmailIntentDialog.tsx b/src/components/intents/VerifyEmailIntentDialog.tsx index da59dc0e6f..36f98cb186 100644 --- a/src/components/intents/VerifyEmailIntentDialog.tsx +++ b/src/components/intents/VerifyEmailIntentDialog.tsx @@ -11,7 +11,7 @@ import * as Dialog from '#/components/Dialog' import {type DialogControlProps} from '#/components/Dialog' import {useConfirmEmail} from '#/components/dialogs/EmailDialog/data/useConfirmEmail' import {Divider} from '#/components/Divider' -import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as Resend} from '#/components/icons/ArrowRotateCounterClockwise' +import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as Resend} from '#/components/icons/ArrowRotate' import {useIntentDialogs} from '#/components/intents/IntentDialogs' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' diff --git a/src/components/moderation/ReportDialog/index.tsx b/src/components/moderation/ReportDialog/index.tsx index 2331db3ea6..3b7cd916e8 100644 --- a/src/components/moderation/ReportDialog/index.tsx +++ b/src/components/moderation/ReportDialog/index.tsx @@ -18,7 +18,7 @@ import * as Admonition from '#/components/Admonition' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {useDelayedLoading} from '#/components/hooks/useDelayedLoading' -import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as Retry} from '#/components/icons/ArrowRotateCounterClockwise' +import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as Retry} from '#/components/icons/ArrowRotate' import { Check_Stroke2_Corner0_Rounded as CheckThin, CheckThick_Stroke2_Corner0_Rounded as Check, diff --git a/src/lib/international-telephone-codes.ts b/src/lib/international-telephone-codes.ts index 355769b47b..b7368e8886 100644 --- a/src/lib/international-telephone-codes.ts +++ b/src/lib/international-telephone-codes.ts @@ -1,3 +1,14 @@ +import {type CountryCode as LibPhoneNumberJsCountryCode} from 'libphonenumber-js' + +// Exclude Ascension Island and Tristan da Cunha - merged into `SH` in 2009 +export type CountryCode = Exclude + +/** + * Note: data is from Wikipedia, but some have been removed to match `libphonenumber-js` + * Mostly tiny British overseas territories + Antarctica, all of which + * share codes with a larger country. If you've one of the 10 people from these + * places, you probably know what to do. + */ export const INTERNATIONAL_TELEPHONE_CODES = { AD: { code: '+376', @@ -34,11 +45,13 @@ export const INTERNATIONAL_TELEPHONE_CODES = { unicodeFlag: '🇦🇴', svgFlag: require('../../assets/icons/flags/AO.svg'), }, - AQ: { - code: '+672', - unicodeFlag: '🇦🇶', - svgFlag: require('../../assets/icons/flags/AQ.svg'), - }, + // sorry penguins :( + // same as Norfolk Island + // AQ: { + // code: '+672', + // unicodeFlag: '🇦🇶', + // svgFlag: require('../../assets/icons/flags/AQ.svg'), + // }, AR: { code: '+54', unicodeFlag: '🇦🇷', @@ -154,11 +167,12 @@ export const INTERNATIONAL_TELEPHONE_CODES = { unicodeFlag: '🇧🇹', svgFlag: require('../../assets/icons/flags/BT.svg'), }, - BV: { - code: '+47', - unicodeFlag: '🇧🇻', - svgFlag: require('../../assets/icons/flags/BV.svg'), - }, + // same as Norway + // BV: { + // code: '+47', + // unicodeFlag: '🇧🇻', + // svgFlag: require('../../assets/icons/flags/BV.svg'), + // }, BW: { code: '+267', unicodeFlag: '🇧🇼', @@ -379,11 +393,12 @@ export const INTERNATIONAL_TELEPHONE_CODES = { unicodeFlag: '🇬🇷', svgFlag: require('../../assets/icons/flags/GR.svg'), }, - GS: { - code: '+500', - unicodeFlag: '🇬🇸', - svgFlag: require('../../assets/icons/flags/GS.svg'), - }, + // same as Falkland Islands + // GS: { + // code: '+500', + // unicodeFlag: '🇬🇸', + // svgFlag: require('../../assets/icons/flags/GS.svg'), + // }, GT: { code: '+502', unicodeFlag: '🇬🇹', @@ -779,11 +794,12 @@ export const INTERNATIONAL_TELEPHONE_CODES = { unicodeFlag: '🇵🇲', svgFlag: require('../../assets/icons/flags/PM.svg'), }, - PN: { - code: '+64', - unicodeFlag: '🇵🇳', - svgFlag: require('../../assets/icons/flags/PN.svg'), - }, + // same as New Zealand + // PN: { + // code: '+64', + // unicodeFlag: '🇵🇳', + // svgFlag: require('../../assets/icons/flags/PN.svg'), + // }, PR: { code: '+1', unicodeFlag: '🇵🇷', @@ -1199,11 +1215,12 @@ export const INTERNATIONAL_TELEPHONE_CODES = { unicodeFlag: '🇫🇴', svgFlag: require('../../assets/icons/flags/FO.svg'), }, - HM: { - code: '+672', - unicodeFlag: '🇭🇲', - svgFlag: require('../../assets/icons/flags/HM.svg'), - }, + // same as Norfolk Island + // HM: { + // code: '+672', + // unicodeFlag: '🇭🇲', + // svgFlag: require('../../assets/icons/flags/HM.svg'), + // }, KM: { code: '+269', unicodeFlag: '🇰🇲', @@ -1229,16 +1246,18 @@ export const INTERNATIONAL_TELEPHONE_CODES = { unicodeFlag: '🇹🇨', svgFlag: require('../../assets/icons/flags/TC.svg'), }, - TF: { - code: '+672', - unicodeFlag: '🇹🇫', - svgFlag: require('../../assets/icons/flags/TF.svg'), - }, - UM: { - code: '+1', - unicodeFlag: '🇺🇲', - svgFlag: require('../../assets/icons/flags/UM.svg'), - }, + // same as Norfolk Island + // TF: { + // code: '+672', + // unicodeFlag: '🇹🇫', + // svgFlag: require('../../assets/icons/flags/TF.svg'), + // }, + // same as US mainland + // UM: { + // code: '+1', + // unicodeFlag: '🇺🇲', + // svgFlag: require('../../assets/icons/flags/UM.svg'), + // }, VA: { code: '+39', unicodeFlag: '🇻🇦', @@ -1249,25 +1268,26 @@ export const INTERNATIONAL_TELEPHONE_CODES = { unicodeFlag: '🇽🇰', svgFlag: require('../../assets/icons/flags/XK.svg'), }, -} +} satisfies Record< + CountryCode, + { + code: string + unicodeFlag: string + svgFlag: any + } +> -const DEFAULT_PHONE_COUNTRY = 'US' +const DEFAULT_PHONE_COUNTRY = 'US' as const -export function getDefaultCountry(location?: {countryCode?: string}) { +export function getDefaultCountry(location?: { + countryCode?: string +}): CountryCode { + const locationCountryCode = location?.countryCode?.toUpperCase() if ( - location?.countryCode && - location.countryCode.toUpperCase() in INTERNATIONAL_TELEPHONE_CODES + locationCountryCode && + locationCountryCode in INTERNATIONAL_TELEPHONE_CODES ) { - return location.countryCode.toUpperCase() + return locationCountryCode as CountryCode } return DEFAULT_PHONE_COUNTRY } - -export function getPhoneCodeFromCountryCode(countryCode: string) { - const country = - INTERNATIONAL_TELEPHONE_CODES[ - countryCode.toUpperCase() as keyof typeof INTERNATIONAL_TELEPHONE_CODES - ] - if (!country) throw new Error(`Country ${countryCode} not found`) - return country.code -} diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index c315a83418..0a3a0d5458 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -67,6 +67,7 @@ export type CommonNavigatorParams = { InterestsSettings: undefined AboutSettings: undefined AppIconSettings: undefined + FindContactsSettings: undefined Search: {q?: string; tab?: 'user' | 'profile' | 'feed'} Hashtag: {tag: string; author?: string} Topic: {topic: string} @@ -87,6 +88,7 @@ export type CommonNavigatorParams = { StarterPackEdit: {rkey?: string} VideoFeed: VideoFeedSourceContext Bookmarks: undefined + FindContactsFlow: undefined } export type BottomTabNavigatorParams = CommonNavigatorParams & { diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index ea263e22e5..8c757a0160 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -3,6 +3,7 @@ export type Gate = | 'alt_share_icon' | 'debug_show_feedcontext' | 'debug_subscriptions' + | 'disable_onboarding_find_contacts' | 'explore_show_suggested_feeds' | 'feed_reply_button_open_thread' | 'old_postonboarding' diff --git a/src/logger/metrics.ts b/src/logger/metrics.ts index 43690f1f7c..6908f3a05e 100644 --- a/src/logger/metrics.ts +++ b/src/logger/metrics.ts @@ -309,6 +309,7 @@ export type MetricEvents = { | 'ImmersiveVideo' | 'ExploreSuggestedAccounts' | 'OnboardingSuggestedAccounts' + | 'FindContacts' } 'profile:followers:view': { contextProfileDid: string @@ -396,6 +397,7 @@ export type MetricEvents = { | 'ImmersiveVideo' | 'ExploreSuggestedAccounts' | 'OnboardingSuggestedAccounts' + | 'FindContacts' } 'chat:create': { logContext: 'ProfileHeader' | 'NewChatDialog' | 'SendViaChatDialog' @@ -625,4 +627,91 @@ export type MetricEvents = { 'blockedGeoOverlay:shown': {} 'geo:debug': {} + + /* + * Find Contacts stuff + */ + + // user presses the button on the new feature NUX + 'contacts:nux:ctaPressed': {} + // user presses the banner NUX + 'contacts:nux:bannerPressed': {} + // user dismisses the banner + 'contacts:nux:bannerDismissed': {} + + // user lands on the contacts step + 'onboarding:contacts:presented': {} + // user pressed "Import Contacts" button to begin flow + 'onboarding:contacts:begin': {} + // skips the step entirely + 'onboarding:contacts:skipPressed': {} + // user shared their contacts + 'onboarding:contacts:contactsShared': {} + // user leaves the matches page + 'onboarding:contacts:nextPressed': { + matchCount: number + followCount: number + dismissedMatchCount: number + } + + // user entered a number + 'contacts:phone:phoneEntered': { + entryPoint: 'Onboarding' | 'Standalone' + } + // user entered the correct one-time-code + 'contacts:phone:phoneVerified': { + entryPoint: 'Onboarding' | 'Standalone' + } + // user responded to the contacts permission prompt + 'contacts:permission:request': { + status: 'granted' | 'denied' + accessLevelIOS?: 'all' | 'limited' | 'none' + } + // contacts were successfully imported and matched + 'contacts:import:success': { + contactCount: number + matchCount: number + entryPoint: 'Onboarding' | 'Standalone' + } + // contacts import failed + 'contacts:import:failure': { + reason: 'noValidNumbers' | 'networkError' | 'unknown' + entryPoint: 'Onboarding' | 'Standalone' + } + // user followed a single match + 'contacts:matches:follow': { + entryPoint: 'Onboarding' | 'Standalone' + } + // user pressed "Follow All" on matches + 'contacts:matches:followAll': { + followCount: number + entryPoint: 'Onboarding' | 'Standalone' + } + // user dismissed a match + 'contacts:matches:dismiss': { + entryPoint: 'Onboarding' | 'Standalone' + } + // user pressed invite to send an SMS to a non-match + 'contacts:matches:invite': { + entryPoint: 'Onboarding' | 'Standalone' + } + // user opened the Find Contacts settings screen + 'contacts:settings:presented': { + hasPreviouslySynced: boolean + matchCount?: number + } + // user followed a single match from settings + 'contacts:settings:follow': {} + // user pressed "Follow All" from settings + 'contacts:settings:followAll': { + followCount: number + } + // user dismissed a match from settings + 'contacts:settings:dismiss': {} + // user re-entered the flow via the resync button + 'contacts:settings:resync': { + daysSinceLastSync: number + } + // user pressed the remove all data button + 'contacts:settings:removeData': {} } diff --git a/src/routes.ts b/src/routes.ts index 1ed913bb29..614b248722 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -69,6 +69,7 @@ export const router = new Router({ '/settings/notifications/reposts-on-reposts', ActivityNotificationSettings: '/settings/notifications/activity', MiscellaneousNotificationSettings: '/settings/notifications/miscellaneous', + FindContactsSettings: '/settings/find-contacts', // support Support: '/support', PrivacyPolicy: '/support/privacy', @@ -91,4 +92,5 @@ export const router = new Router({ StarterPackWizard: '/starter-pack/create', VideoFeed: '/video-feed', Bookmarks: '/saved', + FindContactsFlow: '/find-contacts', }) diff --git a/src/screens/FindContactsFlowScreen.tsx b/src/screens/FindContactsFlowScreen.tsx new file mode 100644 index 0000000000..43fab79b5f --- /dev/null +++ b/src/screens/FindContactsFlowScreen.tsx @@ -0,0 +1,77 @@ +import {useCallback, useLayoutEffect, useState} from 'react' +import {LayoutAnimationConfig} from 'react-native-reanimated' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {usePreventRemove} from '@react-navigation/native' + +import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController' +import { + type AllNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {isNative} from '#/platform/detection' +import {useSetMinimalShellMode} from '#/state/shell' +import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' +import {FindContactsFlow} from '#/components/contacts/FindContactsFlow' +import {useFindContactsFlowState} from '#/components/contacts/state' +import * as Layout from '#/components/Layout' +import {ScreenTransition} from '#/components/ScreenTransition' + +type Props = NativeStackScreenProps +export function FindContactsFlowScreen({navigation}: Props) { + const {_} = useLingui() + + const [state, dispatch] = useFindContactsFlowState() + + const [transitionDirection, setTransitionDirection] = useState< + 'Forward' | 'Backward' + >('Forward') + + const overrideGoBack = state.step === '2: verify number' + + usePreventRemove(overrideGoBack, () => { + setTransitionDirection('Backward') + dispatch({type: 'BACK'}) + setTimeout(() => { + setTransitionDirection('Forward') + }) + }) + + useEnableKeyboardControllerScreen(true) + + const setMinimalShellMode = useSetMinimalShellMode() + const effect = useCallback(() => { + setMinimalShellMode(true) + return () => setMinimalShellMode(false) + }, [setMinimalShellMode]) + useLayoutEffect(effect) + + return ( + + {isNative ? ( + + + + navigation.canGoBack() + ? navigation.goBack() + : navigation.navigate('FindContactsFlow', undefined, { + pop: true, + }) + } + context="Standalone" + /> + + + ) : ( + + )} + + ) +} diff --git a/src/screens/Messages/ChatList.tsx b/src/screens/Messages/ChatList.tsx index 906fcb017a..696c9b8122 100644 --- a/src/screens/Messages/ChatList.tsx +++ b/src/screens/Messages/ChatList.tsx @@ -29,7 +29,7 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {type DialogControlProps, useDialogControl} from '#/components/Dialog' import {NewChat} from '#/components/dms/dialogs/NewChatDialog' import {useRefreshOnFocus} from '#/components/hooks/useRefreshOnFocus' -import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as RetryIcon} from '#/components/icons/ArrowRotateCounterClockwise' +import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as RetryIcon} from '#/components/icons/ArrowRotate' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo' import {Message_Stroke2_Corner0_Rounded as MessageIcon} from '#/components/icons/Message' import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus' diff --git a/src/screens/Messages/Inbox.tsx b/src/screens/Messages/Inbox.tsx index 6d2fc1cd82..433d2ccd62 100644 --- a/src/screens/Messages/Inbox.tsx +++ b/src/screens/Messages/Inbox.tsx @@ -37,7 +37,7 @@ import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {useRefreshOnFocus} from '#/components/hooks/useRefreshOnFocus' import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow' -import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as RetryIcon} from '#/components/icons/ArrowRotateCounterClockwise' +import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as RetryIcon} from '#/components/icons/ArrowRotate' import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo' import {Message_Stroke2_Corner0_Rounded as MessageIcon} from '#/components/icons/Message' diff --git a/src/screens/Onboarding/Layout.tsx b/src/screens/Onboarding/Layout.tsx index 84448d97d4..0fcc5488da 100644 --- a/src/screens/Onboarding/Layout.tsx +++ b/src/screens/Onboarding/Layout.tsx @@ -1,13 +1,12 @@ -import React, {useState} from 'react' +import {useEffect, useRef, useState} from 'react' import {ScrollView, View} from 'react-native' -import {Dimensions} from 'react-native' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {msg} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {isAndroid, isWeb} from '#/platform/detection' import {useOnboardingDispatch} from '#/state/shell' -import {Context} from '#/screens/Onboarding/state' +import {useOnboardingInternalState} from '#/screens/Onboarding/state' import { atoms as a, native, @@ -22,6 +21,7 @@ import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeft} from '#/components/icons import {HEADER_SLOT_SIZE} from '#/components/Layout' import {createPortalGroup} from '#/components/Portal' import {P, Text} from '#/components/Typography' +import {IS_INTERNAL} from '#/env' const ONBOARDING_COL_WIDTH = 420 @@ -34,20 +34,20 @@ export function Layout({children}: React.PropsWithChildren<{}>) { const insets = useSafeAreaInsets() const {gtMobile} = useBreakpoints() const onboardDispatch = useOnboardingDispatch() - const {state, dispatch} = React.useContext(Context) - const scrollview = React.useRef(null) - const prevActiveStep = React.useRef(state.activeStep) + const {state, dispatch} = useOnboardingInternalState() + const scrollview = useRef(null) + const prevActiveStep = useRef(state.activeStep) - React.useEffect(() => { + useEffect(() => { if (state.activeStep !== prevActiveStep.current) { prevActiveStep.current = state.activeStep scrollview.current?.scrollTo({y: 0, animated: false}) } }, [state]) - const paddingTop = gtMobile ? a.py_5xl : a.py_lg const dialogLabel = _(msg`Set up your account`) + const [headerHeight, setHeaderHeight] = useState(0) const [footerHeight, setFooterHeight] = useState(0) return ( @@ -59,33 +59,12 @@ export function Layout({children}: React.PropsWithChildren<{}>) { accessibilityLabel={dialogLabel} accessibilityHint={_(msg`Customizes your Bluesky experience`)} style={[isWeb ? a.fixed : a.absolute, a.inset_0, a.flex_1, t.atoms.bg]}> - {__DEV__ && ( - - )} - - {!gtMobile && ( + {!gtMobile ? ( ) { a.justify_center, a.z_20, a.px_xl, - {top: paddingTop.paddingTop + insets.top - 1}, - ]}> + {paddingTop: (web(tokens.space.lg) ?? 0) + insets.top}, + native([t.atoms.bg, a.pb_xs, {minHeight: 48}]), + web(a.pointer_events_box_none), + ]} + onLayout={evt => setHeaderHeight(evt.nativeEvent.layout.height)}> - {state.hasPrev ? ( + + {state.canGoBack && ( + + )} + + + {IS_INTERNAL && ( - ) : ( - )} - + + + + ) : ( + <> + {IS_INTERNAL && ( + + + + )} + )} + dataSet={{'stable-gutters': 1}} + centerContent={gtMobile}> - - - - {Array(state.totalSteps) - .fill(0) - .map((__, i) => ( - - ))} - - - - - {children} - - - + + {children} @@ -200,7 +188,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) { gtMobile && [a.flex_row, a.justify_between, a.align_center], ]}> {gtMobile && - (state.hasPrev ? ( + (state.canGoBack ? ( + + + + + ) +} diff --git a/src/screens/Onboarding/StepFindContactsIntro/index.web.tsx b/src/screens/Onboarding/StepFindContactsIntro/index.web.tsx new file mode 100644 index 0000000000..bb58f202b9 --- /dev/null +++ b/src/screens/Onboarding/StepFindContactsIntro/index.web.tsx @@ -0,0 +1,3 @@ +export function StepFindContactsIntro() { + throw new Error('StepFindContactsIntro is not available on web') +} diff --git a/src/screens/Onboarding/StepFinished/index.tsx b/src/screens/Onboarding/StepFinished/index.tsx index c1cb3f02f0..abf21d51d5 100644 --- a/src/screens/Onboarding/StepFinished/index.tsx +++ b/src/screens/Onboarding/StepFinished/index.tsx @@ -1,4 +1,4 @@ -import {useCallback, useContext, useState} from 'react' +import {useCallback, useState} from 'react' import {View} from 'react-native' import { type AppBskyActorDefs, @@ -35,28 +35,23 @@ import { useSetActiveStarterPack, } from '#/state/shell/starter-pack' import { - DescriptionText, OnboardingControls, OnboardingHeaderSlot, - TitleText, } from '#/screens/Onboarding/Layout' -import {Context, type OnboardingState} from '#/screens/Onboarding/state' +import { + type OnboardingState, + useOnboardingInternalState, +} from '#/screens/Onboarding/state' import {bulkWriteFollows} from '#/screens/Onboarding/util' -import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {atoms as a, useBreakpoints} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {IconCircle} from '#/components/IconCircle' import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRight} from '#/components/icons/Arrow' -import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' -import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth' -import {News2_Stroke2_Corner0_Rounded as News} from '#/components/icons/News2' -import {Trending2_Stroke2_Corner2_Rounded as Trending} from '#/components/icons/Trending' import {Loader} from '#/components/Loader' -import {Text} from '#/components/Typography' import * as bsky from '#/types/bsky' import {ValuePropositionPager} from './ValuePropositionPager' export function StepFinished() { - const {state, dispatch} = useContext(Context) + const {state, dispatch} = useOnboardingInternalState() const onboardDispatch = useOnboardingDispatch() const [saving, setSaving] = useState(false) const queryClient = useQueryClient() @@ -247,18 +242,12 @@ export function StepFinished() { gate, ]) - return state.experiments?.onboarding_value_prop ? ( + return ( - ) : ( - ) } @@ -359,90 +348,3 @@ function ValueProposition({ ) } - -function LegacyFinalStep({ - finishOnboarding, - saving, - state, -}: { - finishOnboarding: () => void - saving: boolean - state: OnboardingState -}) { - const t = useTheme() - const {_} = useLingui() - - return ( - - - - - You're ready to go! - - - We hope you have a wonderful time. Remember, Bluesky is: - - - - - - - - Public - - - - Your posts, likes, and blocks are public. Mutes are private. - - - - - - - - - Open - - - Never lose access to your followers or data. - - - - - - - - Flexible - - - Choose the algorithms that power your custom feeds. - - - - - - - - - - ) -} diff --git a/src/screens/Onboarding/StepInterests/index.tsx b/src/screens/Onboarding/StepInterests/index.tsx index 3e51409757..c859930916 100644 --- a/src/screens/Onboarding/StepInterests/index.tsx +++ b/src/screens/Onboarding/StepInterests/index.tsx @@ -8,25 +8,23 @@ import {logEvent} from '#/lib/statsig/statsig' import {capitalize} from '#/lib/strings/capitalize' import {logger} from '#/logger' import { - DescriptionText, OnboardingControls, - TitleText, + OnboardingDescriptionText, + OnboardingPosition, + OnboardingTitleText, } from '#/screens/Onboarding/Layout' -import {Context} from '#/screens/Onboarding/state' +import {useOnboardingInternalState} from '#/screens/Onboarding/state' import {InterestButton} from '#/screens/Onboarding/StepInterests/InterestButton' import {atoms as a} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Toggle from '#/components/forms/Toggle' -import {IconCircle} from '#/components/IconCircle' -import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' -import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag' import {Loader} from '#/components/Loader' export function StepInterests() { const {_} = useLingui() const interestsDisplayNames = useInterestsDisplayNames() - const {state, dispatch} = React.useContext(Context) + const {state, dispatch} = useOnboardingInternalState() const [saving, setSaving] = React.useState(false) const [selectedInterests, setSelectedInterests] = React.useState( state.interestsStepResults.selectedInterests.map(i => i), @@ -53,17 +51,16 @@ export function StepInterests() { }, [selectedInterests, setSaving, dispatch]) return ( - - - - + + + What are your interests? - - + + We'll use this to help customize your experience. - + - + Continue - + {saving && } diff --git a/src/screens/Onboarding/StepProfile/index.tsx b/src/screens/Onboarding/StepProfile/index.tsx index 453184639a..787711bf48 100644 --- a/src/screens/Onboarding/StepProfile/index.tsx +++ b/src/screens/Onboarding/StepProfile/index.tsx @@ -19,11 +19,12 @@ import {isCancelledError} from '#/lib/strings/errors' import {logger} from '#/logger' import {isNative, isWeb} from '#/platform/detection' import { - DescriptionText, OnboardingControls, - TitleText, + OnboardingDescriptionText, + OnboardingPosition, + OnboardingTitleText, } from '#/screens/Onboarding/Layout' -import {Context} from '#/screens/Onboarding/state' +import {useOnboardingInternalState} from '#/screens/Onboarding/state' import {AvatarCircle} from '#/screens/Onboarding/StepProfile/AvatarCircle' import {AvatarCreatorCircle} from '#/screens/Onboarding/StepProfile/AvatarCreatorCircle' import {AvatarCreatorItems} from '#/screens/Onboarding/StepProfile/AvatarCreatorItems' @@ -32,13 +33,10 @@ import { type PlaceholderCanvasRef, } from '#/screens/Onboarding/StepProfile/PlaceholderCanvas' import {atoms as a, useBreakpoints, useTheme} from '#/alf' -import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper' -import {IconCircle} from '#/components/IconCircle' -import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' import {CircleInfo_Stroke2_Corner0_Rounded} from '#/components/icons/CircleInfo' -import {StreamingLive_Stroke2_Corner0_Rounded as StreamingLive} from '#/components/icons/StreamingLive' import {Text} from '#/components/Typography' import {type AvatarColor, avatarColors, type Emoji, emojiItems} from './types' @@ -78,7 +76,7 @@ export function StepProfile() { const creatorControl = Dialog.useDialogControl() const [error, setError] = React.useState('') - const {state, dispatch} = React.useContext(Context) + const {state, dispatch} = useOnboardingInternalState() const [avatar, setAvatar] = React.useState({ image: state.profileStepResults?.image, placeholder: state.profileStepResults.creatorState?.emoji || emojiItems.at, @@ -237,19 +235,21 @@ export function StepProfile() { return ( - - - - Give your profile a face - - - - Help people know you're not a bot by uploading a picture or creating - an avatar. - - + + + + + Give your profile a face + + + + Help people know you're not a bot by uploading a picture or + creating an avatar. + + + + style={[a.w_full, a.align_center, {paddingTop: gtMobile ? 80 : 60}]}> + )} + + + + ) +} + +function StatusHeader({ + numMatches, + isPending, + isAnyUnfollowed, +}: { + numMatches: number + isPending: boolean + isAnyUnfollowed: boolean +}) { + const {_} = useLingui() + const agent = useAgent() + const queryClient = useQueryClient() + const {currentAccount} = useSession() + + const { + mutate: onFollowAll, + isPending: isFollowingAll, + isSuccess: hasFollowedAll, + } = useMutation({ + mutationFn: async () => { + const didsToFollow = [] + + let cursor: string | undefined + do { + const page = await agent.app.bsky.contact.getMatches({ + limit: 100, + cursor, + }) + cursor = page.data.cursor + for (const profile of page.data.matches) { + if ( + profile.did !== currentAccount?.did && + !isBlockedOrBlocking(profile) && + !isMuted(profile) && + !profile.viewer?.following + ) { + didsToFollow.push(profile.did) + } + } + } while (cursor) + + logger.metric('contacts:settings:followAll', { + followCount: didsToFollow.length, + }) + + const uris = await wait(500, bulkWriteFollows(agent, didsToFollow)) + + for (const did of didsToFollow) { + const uri = uris.get(did) + updateProfileShadow(queryClient, did, { + followingUri: uri, + }) + } + }, + onSuccess: () => { + Toast.show(_(msg`Followed all matches`), {type: 'success'}) + }, + onError: err => { + if (isNetworkError(err)) { + Toast.show( + _( + msg`Could not follow all matches - please check your network connection.`, + ), + {type: 'error'}, + ) + } else { + logger.error('Failed to follow all matches', {safeMessage: err}) + Toast.show(_(msg`Could not follow all matches. ${cleanError(err)}`), { + type: 'error', + }) + } + }, + }) + + if (numMatches > 0) { + if (isPending) { + return ( + + + + ) + } + + return ( + + + + + {isAnyUnfollowed && ( + + )} + + ) + } + + return null +} + +function StatusFooter({syncedAt}: {syncedAt: string}) { + const {_, i18n} = useLingui() + const t = useTheme() + const agent = useAgent() + const queryClient = useQueryClient() + + const {mutate: removeData, isPending} = useMutation({ + mutationFn: async () => { + await agent.app.bsky.contact.removeData({}) + }, + onMutate: () => logger.metric('contacts:settings:removeData', {}), + onSuccess: () => { + Toast.show(_(msg`Contacts removed`)) + queryClient.setQueryData( + findContactsStatusQueryKey, + {syncStatus: undefined}, + ) + }, + onError: err => { + if (isNetworkError(err)) { + Toast.show( + _( + msg`Failed to remove data due to a network error, please check your internet connection.`, + ), + {type: 'error'}, + ) + } else { + logger.error('Remove data failed', {safeMessage: err}) + Toast.show(_(msg`Failed to remove data. ${cleanError(err)}`), { + type: 'error', + }) + } + }, + }) + + return ( + + + + Contacts uploaded + + + + We will notify you when we find your friends. + + + + Uploaded on{' '} + {i18n.date(new Date(syncedAt), { + dateStyle: 'long', + })} + + + + { + const daysSinceLastSync = Math.floor( + (Date.now() - new Date(syncedAt).getTime()) / + (1000 * 60 * 60 * 24), + ) + logger.metric('contacts:settings:resync', { + daysSinceLastSync, + }) + }} + size="small" + color="primary_subtle" + style={[a.mt_xs]}> + + + Resync contacts + + + + + + + Delete contacts + + + + Bluesky stores your contacts as encoded data. Removing your contacts + will immediately delete this data. + + + + + + ) +} diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx index 1703036b51..6b0e184c03 100644 --- a/src/screens/Settings/Settings.tsx +++ b/src/screens/Settings/Settings.tsx @@ -37,6 +37,7 @@ import {atoms as a, platform, tokens, useBreakpoints, useTheme} from '#/alf' import {AgeAssuranceDismissibleNotice} from '#/components/ageAssurance/AgeAssuranceDismissibleNotice' import {AvatarStackWithFetch} from '#/components/AvatarStack' import {Button, ButtonText} from '#/components/Button' +import {useIsFindContactsFeatureEnabledBasedOnGeolocation} from '#/components/contacts/country-allowlist' import {useDialogControl} from '#/components/Dialog' import {SwitchAccountDialog} from '#/components/dialogs/SwitchAccount' import {Accessibility_Stroke2_Corner2_Rounded as AccessibilityIcon} from '#/components/icons/Accessibility' @@ -45,6 +46,7 @@ import {BubbleInfo_Stroke2_Corner2_Rounded as BubbleInfoIcon} from '#/components import {ChevronTop_Stroke2_Corner0_Rounded as ChevronUpIcon} from '#/components/icons/Chevron' import {CircleQuestion_Stroke2_Corner2_Rounded as CircleQuestionIcon} from '#/components/icons/CircleQuestion' import {CodeBrackets_Stroke2_Corner2_Rounded as CodeBracketsIcon} from '#/components/icons/CodeBrackets' +import {Contacts_Stroke2_Corner2_Rounded as ContactsIcon} from '#/components/icons/Contacts' import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid' import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe' import {Lock_Stroke2_Corner2_Rounded as LockIcon} from '#/components/icons/Lock' @@ -89,6 +91,8 @@ export function SettingsScreen({}: Props) { const {pendingDid, onPressSwitchAccount} = useAccountSwitcher() const [showAccounts, setShowAccounts] = useState(false) const [showDevOptions, setShowDevOptions] = useState(false) + const findContactsEnabled = + useIsFindContactsFeatureEnabledBasedOnGeolocation() return ( @@ -207,6 +211,16 @@ export function SettingsScreen({}: Props) { Content and media + {isNative && findContactsEnabled && ( + + + + Find friends from contacts + + + )} diff --git a/src/screens/Settings/components/OTAInfo.tsx b/src/screens/Settings/components/OTAInfo.tsx index ee63d150fe..8b7ca9c239 100644 --- a/src/screens/Settings/components/OTAInfo.tsx +++ b/src/screens/Settings/components/OTAInfo.tsx @@ -5,7 +5,7 @@ import {useMutation, useQuery} from '@tanstack/react-query' import * as Toast from '#/view/com/util/Toast' import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as RetryIcon} from '#/components/icons/ArrowRotateCounterClockwise' +import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as RetryIcon} from '#/components/icons/ArrowRotate' import {Shapes_Stroke2_Corner0_Rounded as ShapesIcon} from '#/components/icons/Shapes' import {Loader} from '#/components/Loader' import * as SettingsList from '../components/SettingsList' diff --git a/src/state/cache/profile-shadow.ts b/src/state/cache/profile-shadow.ts index 5ee7c9c423..0fdef08ad0 100644 --- a/src/state/cache/profile-shadow.ts +++ b/src/state/cache/profile-shadow.ts @@ -7,6 +7,7 @@ import {batchedUpdates} from '#/lib/batchedUpdates' import {findAllProfilesInQueryData as findAllProfilesInActivitySubscriptionsQueryData} from '#/state/queries/activity-subscriptions' import {findAllProfilesInQueryData as findAllProfilesInActorSearchQueryData} from '#/state/queries/actor-search' import {findAllProfilesInQueryData as findAllProfilesInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews' +import {findAllProfilesInQueryData as findAllProfilesInContactMatchesQueryData} from '#/state/queries/find-contacts' import {findAllProfilesInQueryData as findAllProfilesInKnownFollowersQueryData} from '#/state/queries/known-followers' import {findAllProfilesInQueryData as findAllProfilesInListMembersQueryData} from '#/state/queries/list-members' import {findAllProfilesInQueryData as findAllProfilesInListConvosQueryData} from '#/state/queries/messages/list-conversations' @@ -256,4 +257,5 @@ function* findProfilesInCache( yield* findAllProfilesInExploreFeedPreviewsQueryData(queryClient, did) yield* findAllProfilesInActivitySubscriptionsQueryData(queryClient, did) yield* findAllProfilesInNotifsQueryData(queryClient, did) + yield* findAllProfilesInContactMatchesQueryData(queryClient, did) } diff --git a/src/state/queries/find-contacts.ts b/src/state/queries/find-contacts.ts new file mode 100644 index 0000000000..b1eb6c9c5e --- /dev/null +++ b/src/state/queries/find-contacts.ts @@ -0,0 +1,128 @@ +import {type AppBskyContactGetMatches} from '@atproto/api' +import { + type InfiniteData, + type QueryClient, + useInfiniteQuery, + useQuery, +} from '@tanstack/react-query' + +import {useAgent} from '#/state/session' +import {type Match} from '#/components/contacts/state' +import type * as bsky from '#/types/bsky' +import {STALE} from '.' + +const RQ_KEY_ROOT = 'find-contacts' +export const findContactsStatusQueryKey = [RQ_KEY_ROOT, 'sync-status'] + +export function useContactsSyncStatusQuery() { + const agent = useAgent() + + return useQuery({ + queryKey: findContactsStatusQueryKey, + queryFn: async () => { + const status = await agent.app.bsky.contact.getSyncStatus() + return status.data + }, + staleTime: STALE.SECONDS.THIRTY, + }) +} + +export const findContactsGetMatchesQueryKey = [RQ_KEY_ROOT, 'matches'] + +export function useContactsMatchesQuery() { + const agent = useAgent() + + return useInfiniteQuery({ + queryKey: findContactsGetMatchesQueryKey, + queryFn: async ({pageParam}) => { + const matches = await agent.app.bsky.contact.getMatches({ + cursor: pageParam, + }) + return matches.data + }, + initialPageParam: undefined as string | undefined, + getNextPageParam: lastPage => lastPage.cursor, + staleTime: STALE.MINUTES.ONE, + }) +} + +export function optimisticRemoveMatch(queryClient: QueryClient, did: string) { + queryClient.setQueryData>( + findContactsGetMatchesQueryKey, + old => { + if (!old) return old + + return { + ...old, + pages: old.pages.map(page => ({ + ...page, + matches: page.matches.filter(match => match.did !== did), + })), + } + }, + ) +} + +export const findContactsMatchesPassthroughQueryKey = (dids: string[]) => [ + RQ_KEY_ROOT, + 'passthrough', + dids, +] + +/** + * DIRTY HACK WARNING! + * + * The only way to get shadow state to work is to put it into React Query. + * However, when we get the matches it's via a POST, not a GET, so we use a mutation, + * which means we can't use shadowing! + * + * In lieu of any better ideas, I'm just going to take the contacts we have and + * "launder" them through a dummy query. This will then return "shadow-able" profiles. + */ +export function useMatchesPassthroughQuery(matches: Match[]) { + const dids = matches.map(match => match.profile.did) + const {data} = useQuery({ + queryKey: findContactsMatchesPassthroughQueryKey(dids), + queryFn: () => { + return matches + }, + }) + return data ?? matches +} + +export function* findAllProfilesInQueryData( + queryClient: QueryClient, + did: string, +): Generator { + const queryDatas = queryClient.getQueriesData< + InfiniteData + >({ + queryKey: findContactsGetMatchesQueryKey, + }) + for (const [_queryKey, queryData] of queryDatas) { + if (!queryData?.pages) { + continue + } + for (const page of queryData?.pages) { + for (const match of page.matches) { + if (match.did === did) { + yield match + } + } + } + } + + const passthroughQueryDatas = queryClient.getQueriesData({ + queryKey: [RQ_KEY_ROOT, 'passthrough'], + }) + for (const [_queryKey, queryData] of passthroughQueryDatas) { + if (!queryData) { + continue + } + for (const match of queryData) { + if (match.profile.did === did) { + yield match.profile + } + } + } +} diff --git a/src/state/queries/notifications/feed.ts b/src/state/queries/notifications/feed.ts index 7959c67a76..57d83cb5bb 100644 --- a/src/state/queries/notifications/feed.ts +++ b/src/state/queries/notifications/feed.ts @@ -319,7 +319,10 @@ export function* findAllProfilesInQueryData( } for (const page of queryData?.pages) { for (const item of page.items) { - if (item.type === 'follow' && item.notification.author.did === did) { + if ( + (item.type === 'follow' || item.type === 'contact-match') && + item.notification.author.did === did + ) { yield item.notification.author } else if ( item.type !== 'starterpack-joined' && diff --git a/src/state/queries/notifications/types.ts b/src/state/queries/notifications/types.ts index a7b837086b..1b66dd1759 100644 --- a/src/state/queries/notifications/types.ts +++ b/src/state/queries/notifications/types.ts @@ -49,6 +49,7 @@ type OtherNotificationType = | 'like-via-repost' | 'repost-via-repost' | 'subscribed-post' + | 'contact-match' | 'unknown' type FeedNotificationBase = { diff --git a/src/state/queries/notifications/util.ts b/src/state/queries/notifications/util.ts index a47659a842..a8c15e82c0 100644 --- a/src/state/queries/notifications/util.ts +++ b/src/state/queries/notifications/util.ts @@ -273,7 +273,8 @@ function toKnownType( notif.reason === 'unverified' || notif.reason === 'like-via-repost' || notif.reason === 'repost-via-repost' || - notif.reason === 'subscribed-post' + notif.reason === 'subscribed-post' || + notif.reason === 'contact-match' ) { return notif.reason as NotificationType } diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts index 1656494471..6c03c936a6 100644 --- a/src/state/queries/nuxs/definitions.ts +++ b/src/state/queries/nuxs/definitions.ts @@ -10,6 +10,8 @@ export enum Nux { AgeAssuranceDismissibleNotice = 'AgeAssuranceDismissibleNotice', AgeAssuranceDismissibleFeedBanner = 'AgeAssuranceDismissibleFeedBanner', BookmarksAnnouncement = 'BookmarksAnnouncement', + FindContactsAnnouncement = 'FindContactsAnnouncement', + FindContactsDismissibleBanner = 'FindContactsDismissibleBanner', /* * Blocking announcements. New IDs are required for each new announcement. @@ -52,6 +54,14 @@ export type AppNux = BaseNux< id: Nux.BookmarksAnnouncement data: undefined } + | { + id: Nux.FindContactsAnnouncement + data: undefined + } + | { + id: Nux.FindContactsDismissibleBanner + data: undefined + } > export const NuxSchemas: Record | undefined> = { @@ -63,4 +73,6 @@ export const NuxSchemas: Record | undefined> = { [Nux.AgeAssuranceDismissibleFeedBanner]: undefined, [Nux.PolicyUpdate202508]: undefined, [Nux.BookmarksAnnouncement]: undefined, + [Nux.FindContactsAnnouncement]: undefined, + [Nux.FindContactsDismissibleBanner]: undefined, } diff --git a/src/view/com/notifications/NotificationFeedItem.tsx b/src/view/com/notifications/NotificationFeedItem.tsx index 6d3f57b717..8d622f3bf0 100644 --- a/src/view/com/notifications/NotificationFeedItem.tsx +++ b/src/view/com/notifications/NotificationFeedItem.tsx @@ -1,11 +1,4 @@ -import { - memo, - type ReactElement, - useCallback, - useEffect, - useMemo, - useState, -} from 'react' +import {memo, useCallback, useEffect, useMemo, useState} from 'react' import { Animated, type GestureResponderEvent, @@ -61,6 +54,7 @@ import { ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon, ChevronTop_Stroke2_Corner0_Rounded as ChevronUpIcon, } from '#/components/icons/Chevron' +import {Contacts_Filled_Corner2_Rounded as ContactsIconFilled} from '#/components/icons/Contacts' import {Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled} from '#/components/icons/Heart2' import {PersonPlus_Filled_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person' import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus' @@ -116,6 +110,7 @@ let NotificationFeedItem = ({ break } case 'follow': + case 'contact-match': case 'verified': case 'unverified': { return makeProfileLink(item.notification.author) @@ -279,7 +274,7 @@ let NotificationFeedItem = ({ : '' let a11yLabel = '' - let notificationContent: ReactElement + let notificationContent: React.ReactElement let icon = ( + } else if (item.type === 'contact-match') { + a11yLabel = _(msg`Your contact ${firstAuthorName} is on Bluesky`) + notificationContent = ( + Your contact {firstAuthorLink} is on Bluesky + ) + icon = ( + + ) } else if (item.type === 'feedgen-like') { a11yLabel = hasMultipleAuthors ? _( @@ -438,7 +441,7 @@ let NotificationFeedItem = ({ notificationContent = hasMultipleAuthors ? ( {firstAuthorLink} and{' '} - + {firstAuthorLink} and{' '} - + - {item.type === 'follow' && !hasMultipleAuthors && !isFollowBack ? ( + {(item.type === 'follow' && !hasMultipleAuthors && !isFollowBack) || + (item.type === 'contact-match' && + !item.notification.author.viewer?.following) ? ( ) : null} {item.type === 'post-like' || @@ -809,6 +814,7 @@ function FollowBackButton({profile}: {profile: AppBskyActorDefs.ProfileView}) { } const isFollowing = profileShadow.viewer.following + const isFollowedBy = profileShadow.viewer.followedBy const followingLabel = _( msg({ message: 'Following', @@ -832,14 +838,14 @@ function FollowBackButton({profile}: {profile: AppBskyActorDefs.ProfileView}) { ) : ( )} diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx index 243f3705b5..44d4aeaee8 100644 --- a/src/view/com/profile/ProfileFollows.tsx +++ b/src/view/com/profile/ProfileFollows.tsx @@ -12,6 +12,7 @@ import {isWeb} from '#/platform/detection' import {useProfileFollowsQuery} from '#/state/queries/profile-follows' import {useResolveDidQuery} from '#/state/queries/resolve-uri' import {useSession} from '#/state/session' +import {FindContactsBannerNUX} from '#/components/contacts/FindContactsBannerNUX' import {PeopleRemove2_Stroke1_Corner0_Rounded as PeopleRemoveIcon} from '#/components/icons/PeopleRemove2' import {ListFooter, ListMaybePlaceholder} from '#/components/Lists' import {List} from '../util/List' @@ -208,6 +209,7 @@ export function ProfileFollows({name}: {name: string}) { onEndReached={onEndReached} onEndReachedThreshold={4} onItemSeen={onItemSeen} + ListHeaderComponent={} ListFooterComponent={