about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/dms/MessagesNUX.tsx172
-rw-r--r--src/screens/Messages/List/index.tsx3
-rw-r--r--src/screens/Messages/Settings.tsx4
-rw-r--r--src/state/queries/messages/actor-declaration.ts22
-rw-r--r--src/view/screens/Settings/index.tsx13
5 files changed, 210 insertions, 4 deletions
diff --git a/src/components/dms/MessagesNUX.tsx b/src/components/dms/MessagesNUX.tsx
new file mode 100644
index 000000000..81d1cfff4
--- /dev/null
+++ b/src/components/dms/MessagesNUX.tsx
@@ -0,0 +1,172 @@
+import React, {useCallback, useEffect} from 'react'
+import {View} from 'react-native'
+import {ChatBskyActorDeclaration} from '@atproto/api'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {useUpdateActorDeclaration} from '#/state/queries/messages/actor-declaration'
+import {useProfileQuery} from '#/state/queries/profile'
+import {useSession} from '#/state/session'
+import * as Toast from '#/view/com/util/Toast'
+import {atoms as a, useTheme, web} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import * as Dialog from '#/components/Dialog'
+import * as Toggle from '#/components/forms/Toggle'
+import {Message_Stroke2_Corner0_Rounded} from '#/components/icons/Message'
+import {Text} from '#/components/Typography'
+
+export function MessagesNUX() {
+  const control = Dialog.useDialogControl()
+
+  const {currentAccount} = useSession()
+  const {data: profile} = useProfileQuery({
+    did: currentAccount!.did,
+  })
+
+  useEffect(() => {
+    if (profile && typeof profile.associated?.chat === 'undefined') {
+      const timeout = setTimeout(() => {
+        control.open()
+      }, 1000)
+
+      return () => {
+        clearTimeout(timeout)
+      }
+    }
+  }, [profile, control])
+
+  if (!profile) return null
+
+  return (
+    <Dialog.Outer control={control}>
+      <Dialog.Handle />
+      <DialogInner chatDeclation={profile.associated?.chat} />
+    </Dialog.Outer>
+  )
+}
+
+function DialogInner({
+  chatDeclation,
+}: {
+  chatDeclation?: ChatBskyActorDeclaration.Record
+}) {
+  const control = Dialog.useDialogContext()
+  const {_} = useLingui()
+  const t = useTheme()
+  const {mutate: updateDeclaration} = useUpdateActorDeclaration({
+    onError: () => {
+      Toast.show(_(msg`Failed to update settings`))
+    },
+  })
+
+  const onSelectItem = useCallback(
+    (keys: string[]) => {
+      const key = keys[0]
+      if (!key) return
+      updateDeclaration(key as 'all' | 'none' | 'following')
+    },
+    [updateDeclaration],
+  )
+
+  useEffect(() => {
+    if (!chatDeclation) {
+      updateDeclaration('following')
+    }
+  }, [chatDeclation, updateDeclaration])
+
+  return (
+    <Dialog.ScrollableInner
+      label={_(msg`Introducing Direct Messages`)}
+      style={web({maxWidth: 440})}>
+      <View style={a.gap_xl}>
+        <View style={[a.align_center, a.pt_sm, a.pb_xs]}>
+          <Message_Stroke2_Corner0_Rounded width={64} />
+          <Text style={[a.text_2xl, a.font_bold, a.text_center, a.mt_md]}>
+            <Trans>Direct messages are here!</Trans>
+          </Text>
+          <Text style={[a.text_md, a.text_center, a.mt_sm]}>
+            <Trans>Privately chat with other users.</Trans>
+          </Text>
+        </View>
+        <View
+          style={[
+            a.gap_xs,
+            a.border,
+            a.overflow_hidden,
+            a.rounded_sm,
+            t.atoms.border_contrast_low,
+          ]}>
+          <View
+            style={[
+              a.p_md,
+              a.border_b,
+              t.atoms.bg_contrast_25,
+              t.atoms.border_contrast_low,
+            ]}>
+            <Text style={[a.text_sm, a.font_bold]}>
+              <Trans>Who can message you?</Trans>
+            </Text>
+            <Text
+              style={[
+                a.mt_xs,
+                a.text_sm,
+                a.italic,
+                t.atoms.text_contrast_medium,
+              ]}>
+              <Trans>You can change this at any time.</Trans>
+            </Text>
+          </View>
+          <View style={[a.px_md, a.py_xs]}>
+            <Toggle.Group
+              label={_(msg`Who can message you?`)}
+              type="radio"
+              values={[chatDeclation?.allowIncoming ?? 'following']}
+              onChange={onSelectItem}>
+              <View>
+                <Toggle.Item
+                  name="all"
+                  label={_(msg`Everyone`)}
+                  style={[a.justify_between, a.py_sm, a.rounded_2xs]}>
+                  <Toggle.LabelText>
+                    <Trans>Everyone</Trans>
+                  </Toggle.LabelText>
+                  <Toggle.Radio />
+                </Toggle.Item>
+                <Toggle.Item
+                  name="following"
+                  label={_(msg`Users I follow`)}
+                  style={[a.justify_between, a.py_sm, a.rounded_2xs]}>
+                  <Toggle.LabelText>
+                    <Trans>Users I follow</Trans>
+                  </Toggle.LabelText>
+                  <Toggle.Radio />
+                </Toggle.Item>
+                <Toggle.Item
+                  name="none"
+                  label={_(msg`No one`)}
+                  style={[a.justify_between, a.py_sm, a.rounded_2xs]}>
+                  <Toggle.LabelText>
+                    <Trans>No one</Trans>
+                  </Toggle.LabelText>
+                  <Toggle.Radio />
+                </Toggle.Item>
+              </View>
+            </Toggle.Group>
+          </View>
+        </View>
+        <Button
+          label={_(msg`Start chatting`)}
+          accessibilityHint={_(msg`Close modal`)}
+          size="medium"
+          color="primary"
+          variant="solid"
+          onPress={() => control.close()}>
+          <ButtonText>
+            <Trans>Get started</Trans>
+          </ButtonText>
+        </Button>
+      </View>
+      <Dialog.Close />
+    </Dialog.ScrollableInner>
+  )
+}
diff --git a/src/screens/Messages/List/index.tsx b/src/screens/Messages/List/index.tsx
index 060dac630..e36d1edf2 100644
--- a/src/screens/Messages/List/index.tsx
+++ b/src/screens/Messages/List/index.tsx
@@ -17,6 +17,7 @@ import {CenteredView} from '#/view/com/util/Views'
 import {atoms as a, useBreakpoints, useTheme} from '#/alf'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {DialogControlProps, useDialogControl} from '#/components/Dialog'
+import {MessagesNUX} from '#/components/dms/MessagesNUX'
 import {NewChat} from '#/components/dms/NewChat'
 import {useRefreshOnFocus} from '#/components/hooks/useRefreshOnFocus'
 import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
@@ -131,6 +132,7 @@ export function MessagesScreen({navigation, route}: Props) {
   if (conversations.length < 1) {
     return (
       <View style={a.flex_1}>
+        <MessagesNUX />
         {gtMobile ? (
           <CenteredView sideBorders>
             <DesktopHeader
@@ -165,6 +167,7 @@ export function MessagesScreen({navigation, route}: Props) {
 
   return (
     <View style={a.flex_1}>
+      <MessagesNUX />
       {!gtMobile && (
         <ViewHeader
           title={_(msg`Messages`)}
diff --git a/src/screens/Messages/Settings.tsx b/src/screens/Messages/Settings.tsx
index a9c35dba7..7dbf027f9 100644
--- a/src/screens/Messages/Settings.tsx
+++ b/src/screens/Messages/Settings.tsx
@@ -1,10 +1,8 @@
 import React, {useCallback} from 'react'
 import {View} from 'react-native'
-import {AppBskyActorDefs} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {NativeStackScreenProps} from '@react-navigation/native-stack'
-import {UseQueryResult} from '@tanstack/react-query'
 
 import {CommonNavigatorParams} from '#/lib/routes/types'
 import {useGate} from '#/lib/statsig/statsig'
@@ -30,7 +28,7 @@ export function MessagesSettingsScreen({}: Props) {
   const {currentAccount} = useSession()
   const {data: profile} = useProfileQuery({
     did: currentAccount!.did,
-  }) as UseQueryResult<AppBskyActorDefs.ProfileViewDetailed, Error>
+  })
   const {preferences, setPref} = useBackgroundNotificationPreferences()
 
   const {mutate: updateDeclaration} = useUpdateActorDeclaration({
diff --git a/src/state/queries/messages/actor-declaration.ts b/src/state/queries/messages/actor-declaration.ts
index c8cc4acbd..0886af382 100644
--- a/src/state/queries/messages/actor-declaration.ts
+++ b/src/state/queries/messages/actor-declaration.ts
@@ -21,9 +21,9 @@ export function useUpdateActorDeclaration({
       if (!currentAccount) throw new Error('Not logged in')
       // TODO(sam): remove validate: false once PDSes have the new lexicon
       const result = await getAgent().api.com.atproto.repo.putRecord({
+        repo: currentAccount.did,
         collection: 'chat.bsky.actor.declaration',
         rkey: 'self',
-        repo: currentAccount.did,
         validate: false,
         record: {
           $type: 'chat.bsky.actor.declaration',
@@ -62,3 +62,23 @@ export function useUpdateActorDeclaration({
     },
   })
 }
+
+// for use in the settings screen for testing
+export function useDeleteActorDeclaration() {
+  const {currentAccount} = useSession()
+  const {getAgent} = useAgent()
+
+  return useMutation({
+    mutationFn: async () => {
+      if (!currentAccount) throw new Error('Not logged in')
+      // TODO(sam): remove validate: false once PDSes have the new lexicon
+      const result = await getAgent().api.com.atproto.repo.deleteRecord({
+        repo: currentAccount.did,
+        collection: 'chat.bsky.actor.declaration',
+        rkey: 'self',
+        validate: false,
+      })
+      return result
+    },
+  })
+}
diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx
index c3864e5a9..b3b937c61 100644
--- a/src/view/screens/Settings/index.tsx
+++ b/src/view/screens/Settings/index.tsx
@@ -26,6 +26,7 @@ import {
   useInAppBrowser,
   useSetInAppBrowser,
 } from '#/state/preferences/in-app-browser'
+import {useDeleteActorDeclaration} from '#/state/queries/messages/actor-declaration'
 import {useClearPreferencesMutation} from '#/state/queries/preferences'
 import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
 import {useProfileQuery} from '#/state/queries/profile'
@@ -305,6 +306,8 @@ export function SettingsScreen({}: Props) {
     Toast.show(_(msg`Legacy storage cleared, you need to restart the app now.`))
   }, [_])
 
+  const {mutate: onPressDeleteChatDeclaration} = useDeleteActorDeclaration()
+
   return (
     <View style={s.hContentRegion} testID="settingsScreen">
       <ExportCarDialog control={exportCarControl} />
@@ -828,6 +831,16 @@ export function SettingsScreen({}: Props) {
             </TouchableOpacity>
             <TouchableOpacity
               style={[pal.view, styles.linkCardNoIcon]}
+              onPress={() => onPressDeleteChatDeclaration()}
+              accessibilityRole="button"
+              accessibilityLabel={_(msg`Delete chat declaration record`)}
+              accessibilityHint={_(msg`Deletes the chat declaration record`)}>
+              <Text type="lg" style={pal.text}>
+                <Trans>Delete chat declaration record</Trans>
+              </Text>
+            </TouchableOpacity>
+            <TouchableOpacity
+              style={[pal.view, styles.linkCardNoIcon]}
               onPress={onPressResetOnboarding}
               accessibilityRole="button"
               accessibilityLabel={_(msg`Reset onboarding state`)}