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/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
index 790f211eed..7d78b0fee3 100644
--- a/bskyweb/cmd/bskyweb/server.go
+++ b/bskyweb/cmd/bskyweb/server.go
@@ -308,6 +308,7 @@ 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/sync-contacts", server.WebGeneric)
e.GET("/settings/app-icon", server.WebGeneric)
e.GET("/sys/debug", server.WebGeneric)
e.GET("/sys/debug-mod", server.WebGeneric)
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index fa33a9d56c..1282e7d378 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'
@@ -119,6 +123,7 @@ import {RepostNotificationSettingsScreen} from '#/screens/Settings/NotificationS
import {RepostsOnRepostsNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/RepostsOnRepostsNotificationSettings'
import {PrivacyAndSecuritySettingsScreen} from '#/screens/Settings/PrivacyAndSecuritySettings'
import {SettingsScreen} from '#/screens/Settings/Settings'
+import {SyncContactsSettingsScreen} from '#/screens/Settings/SyncContactsSettings'
import {ThreadPreferencesScreen} from '#/screens/Settings/ThreadPreferences'
import {
StarterPackScreen,
@@ -134,10 +139,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 +417,14 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) {
requireAuth: true,
}}
/>
+ SyncContactsSettingsScreen}
+ options={{
+ title: title(msg`Sync contacts`),
+ requireAuth: true,
+ }}
+ />
NotificationSettingsScreen}
diff --git a/src/components/icons/Contacts.tsx b/src/components/icons/Contacts.tsx
new file mode 100644
index 0000000000..4467a396d5
--- /dev/null
+++ b/src/components/icons/Contacts.tsx
@@ -0,0 +1,5 @@
+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',
+})
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
index c315a83418..ceeef7389e 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
+ SyncContactsSettings: undefined
Search: {q?: string; tab?: 'user' | 'profile' | 'feed'}
Hashtag: {tag: string; author?: string}
Topic: {topic: string}
diff --git a/src/routes.ts b/src/routes.ts
index 1ed913bb29..5afed2c965 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',
+ SyncContactsSettings: '/settings/sync-contacts',
// support
Support: '/support',
PrivacyPolicy: '/support/privacy',
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
index 1703036b51..351a469aab 100644
--- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx
@@ -45,6 +45,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'
@@ -207,6 +208,14 @@ export function SettingsScreen({}: Props) {
Content and media
+
+
+
+ Sync contacts
+
+
diff --git a/src/screens/Settings/SyncContactsSettings.tsx b/src/screens/Settings/SyncContactsSettings.tsx
new file mode 100644
index 0000000000..cb8cd8b418
--- /dev/null
+++ b/src/screens/Settings/SyncContactsSettings.tsx
@@ -0,0 +1,56 @@
+import {Trans} from '@lingui/macro'
+
+import {
+ type AllNavigatorParams,
+ type NativeStackScreenProps,
+} from '#/lib/routes/types'
+import {atoms as a, useTheme} from '#/alf'
+import {Contacts_Stroke2_Corner2_Rounded as SyncContactsIcon} from '#/components/icons/Contacts'
+import * as Layout from '#/components/Layout'
+import {InlineLinkText} from '#/components/Link'
+import {Text} from '#/components/Typography'
+import * as SettingsList from './components/SettingsList'
+
+type Props = NativeStackScreenProps
+export function SyncContactsSettingsScreen({}: Props) {
+ const t = useTheme()
+
+ return (
+
+
+
+
+
+ Sync contacts
+
+
+
+
+
+
+
+
+
+ Sync Contacts
+
+
+
+
+
+ Contacts from your address book will be uploaded to Bluesky on
+ an ongoing basis to help connect you with your friends and
+ personalize content, such as making suggestions for you and
+ others. Turning off syncing will not remove previously uploaded
+ contacts.{' '}
+
+ TODO: Add learn more link
+
+
+
+
+
+
+
+ )
+}