about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-05-14 17:41:20 +0100
committerGitHub <noreply@github.com>2024-05-14 17:41:20 +0100
commitbffb9b590672c1e636083bdf9873f5cd8ab97b57 (patch)
treef4818a5b5b0486d135ce9a3d91abbe5908243b71 /src
parent9173be686c1df9adc6cbb9cc2175f8909a868c35 (diff)
downloadvoidsky-bffb9b590672c1e636083bdf9873f5cd8ab97b57.tar.zst
[🐴] Chat muted state (#3988)
* separate out chatlistitem and add muted icon

* move bell icon to the right of the timeelapsed
Diffstat (limited to 'src')
-rw-r--r--src/components/icons/Bell2.tsx8
-rw-r--r--src/screens/Messages/List/ChatListItem.tsx209
-rw-r--r--src/screens/Messages/List/index.tsx187
3 files changed, 220 insertions, 184 deletions
diff --git a/src/components/icons/Bell2.tsx b/src/components/icons/Bell2.tsx
index 084445b1d..f3d46d09d 100644
--- a/src/components/icons/Bell2.tsx
+++ b/src/components/icons/Bell2.tsx
@@ -7,3 +7,11 @@ export const Bell2_Stroke2_Corner0_Rounded = createSinglePathSVG({
 export const Bell2_Filled_Corner0_Rounded = createSinglePathSVG({
   path: 'M12 2a7.307 7.307 0 0 0-7.298 6.943l-.19 3.798-1.321 2.641A1.809 1.809 0 0 0 4.809 18H7.1a5.002 5.002 0 0 0 9.8 0h2.291a1.809 1.809 0 0 0 1.618-2.618l-1.32-2.641-.19-3.798A7.308 7.308 0 0 0 12 2Zm0 18a3.001 3.001 0 0 1-2.83-2h5.66A3.001 3.001 0 0 1 12 20Z',
 })
+
+export const Bell2Off_Stroke2_Corner0_Rounded = createSinglePathSVG({
+  path: 'M2.293 2.293a1 1 0 0 1 1.414 0l18 18a1 1 0 0 1-1.414 1.414L17.586 19h-.94c-.904 1.748-2.607 3-4.646 3-2.039 0-3.742-1.252-4.646-3H4a1 1 0 0 1-.991-1.132l1.207-9.053c.116-.87.372-1.69.743-2.442L2.293 3.707a1 1 0 0 1 0-1.414Zm4.19 5.604c-.134.376-.23.772-.285 1.183L5.142 17h10.444L6.483 7.897ZM9.778 19c.61.637 1.399 1 2.222 1s1.613-.363 2.222-1H9.778ZM8.834 2.666a7.853 7.853 0 0 1 10.95 6.15l.645 4.832a1 1 0 0 1-1.983.265l-.644-4.833A5.853 5.853 0 0 0 9.64 4.495a1 1 0 0 1-.807-1.83Z',
+})
+
+export const Bell2Off_Filled_Corner0_Rounded = createSinglePathSVG({
+  path: 'm19.785 8.815 1.034 7.761L7.595 3.352a7.853 7.853 0 0 1 12.19 5.463ZM4 19h3.354c.904 1.748 2.607 3 4.646 3 2.038 0 3.742-1.252 4.646-3h.94l2.707 2.707a1 1 0 0 0 1.414-1.414l-18-18a1 1 0 0 0-1.414 1.414l2.666 2.666a7.842 7.842 0 0 0-.743 2.442l-1.207 9.053A1 1 0 0 0 4 19Zm8 1c-.823 0-1.613-.363-2.222-1h4.443c-.608.637-1.398 1-2.221 1Z',
+})
diff --git a/src/screens/Messages/List/ChatListItem.tsx b/src/screens/Messages/List/ChatListItem.tsx
new file mode 100644
index 000000000..13706472f
--- /dev/null
+++ b/src/screens/Messages/List/ChatListItem.tsx
@@ -0,0 +1,209 @@
+import React from 'react'
+import {View} from 'react-native'
+import {ChatBskyConvoDefs} from '@atproto-labs/api'
+import {msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {useNavigation} from '@react-navigation/native'
+
+import {NavigationProp} from '#/lib/routes/types'
+import {isNative} from '#/platform/detection'
+import {useSession} from '#/state/session'
+import {TimeElapsed} from '#/view/com/util/TimeElapsed'
+import {UserAvatar} from '#/view/com/util/UserAvatar'
+import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
+import {Button} from '#/components/Button'
+import {ConvoMenu} from '#/components/dms/ConvoMenu'
+import {Bell2Off_Filled_Corner0_Rounded as BellStroke} from '#/components/icons/Bell2'
+import {useMenuControl} from '#/components/Menu'
+import {Text} from '#/components/Typography'
+
+export function ChatListItem({
+  convo,
+  index,
+}: {
+  convo: ChatBskyConvoDefs.ConvoView
+  index: number
+}) {
+  const t = useTheme()
+  const {_} = useLingui()
+  const {currentAccount} = useSession()
+  const menuControl = useMenuControl()
+  const {gtMobile} = useBreakpoints()
+
+  let lastMessage = _(msg`No messages yet`)
+  let lastMessageSentAt: string | null = null
+  if (ChatBskyConvoDefs.isMessageView(convo.lastMessage)) {
+    if (convo.lastMessage.sender?.did === currentAccount?.did) {
+      lastMessage = _(msg`You: ${convo.lastMessage.text}`)
+    } else {
+      lastMessage = convo.lastMessage.text
+    }
+    lastMessageSentAt = convo.lastMessage.sentAt
+  }
+  if (ChatBskyConvoDefs.isDeletedMessageView(convo.lastMessage)) {
+    lastMessage = _(msg`Message deleted`)
+  }
+
+  const otherUser = convo.members.find(
+    member => member.did !== currentAccount?.did,
+  )
+
+  const navigation = useNavigation<NavigationProp>()
+  const [showActions, setShowActions] = React.useState(false)
+
+  const onMouseEnter = React.useCallback(() => {
+    setShowActions(true)
+  }, [])
+
+  const onMouseLeave = React.useCallback(() => {
+    setShowActions(false)
+  }, [])
+
+  const onFocus = React.useCallback<React.FocusEventHandler>(e => {
+    if (e.nativeEvent.relatedTarget == null) return
+    setShowActions(true)
+  }, [])
+
+  const onPress = React.useCallback(() => {
+    navigation.push('MessagesConversation', {
+      conversation: convo.id,
+    })
+  }, [convo.id, navigation])
+
+  if (!otherUser) {
+    return null
+  }
+
+  return (
+    <View
+      // @ts-expect-error web only
+      onMouseEnter={onMouseEnter}
+      onMouseLeave={onMouseLeave}
+      onFocus={onFocus}
+      onBlur={onMouseLeave}>
+      <Button
+        label={otherUser.displayName || otherUser.handle}
+        onPress={onPress}
+        style={a.flex_1}
+        onLongPress={isNative ? menuControl.open : undefined}>
+        {({hovered, pressed}) => (
+          <View
+            style={[
+              a.flex_row,
+              a.flex_1,
+              a.px_lg,
+              a.py_md,
+              a.gap_md,
+              (hovered || pressed) && t.atoms.bg_contrast_25,
+              index === 0 && [a.border_t, a.pt_lg],
+              t.atoms.border_contrast_low,
+            ]}>
+            <UserAvatar avatar={otherUser?.avatar} size={52} />
+            <View style={[a.flex_1, a.flex_row, a.align_center]}>
+              <View style={[a.flex_1]}>
+                <View
+                  style={[
+                    a.flex_1,
+                    a.flex_row,
+                    a.align_end,
+                    a.pb_2xs,
+                    web([{marginTop: -2}]),
+                  ]}>
+                  <Text
+                    numberOfLines={1}
+                    style={[{maxWidth: '85%'}, web([a.leading_normal])]}>
+                    <Text style={[a.text_md, t.atoms.text, a.font_bold]}>
+                      {otherUser.displayName || otherUser.handle}
+                    </Text>
+                  </Text>
+                  {lastMessageSentAt && (
+                    <TimeElapsed timestamp={lastMessageSentAt}>
+                      {({timeElapsed}) => (
+                        <Text
+                          style={[
+                            a.text_sm,
+                            web([a.leading_normal, {whiteSpace: 'pre'}]),
+                            t.atoms.text_contrast_medium,
+                          ]}>
+                          {' '}
+                          &middot; {timeElapsed}
+                        </Text>
+                      )}
+                    </TimeElapsed>
+                  )}
+                  {convo.muted && (
+                    <Text
+                      style={[
+                        a.text_sm,
+                        web([a.leading_normal, {whiteSpace: 'pre'}]),
+                        t.atoms.text_contrast_medium,
+                      ]}>
+                      {' '}
+                      &middot;{' '}
+                      <BellStroke
+                        size="xs"
+                        style={t.atoms.text_contrast_medium}
+                      />
+                    </Text>
+                  )}
+                </View>
+                <Text
+                  numberOfLines={1}
+                  style={[a.text_sm, t.atoms.text_contrast_medium, a.pb_xs]}>
+                  @{otherUser.handle}
+                </Text>
+                <Text
+                  numberOfLines={2}
+                  style={[
+                    a.text_sm,
+                    a.leading_snug,
+                    convo.unreadCount > 0
+                      ? a.font_bold
+                      : t.atoms.text_contrast_high,
+                    convo.muted && t.atoms.text_contrast_medium,
+                  ]}>
+                  {lastMessage}
+                </Text>
+              </View>
+              {convo.unreadCount > 0 && (
+                <View
+                  style={[
+                    a.absolute,
+                    a.rounded_full,
+                    {
+                      backgroundColor: convo.muted
+                        ? t.palette.contrast_200
+                        : t.palette.primary_500,
+                      height: 7,
+                      width: 7,
+                    },
+                    isNative
+                      ? {
+                          top: 15,
+                          right: 12,
+                        }
+                      : {
+                          top: 0,
+                          right: 0,
+                        },
+                  ]}
+                />
+              )}
+              <ConvoMenu
+                convo={convo}
+                profile={otherUser}
+                control={menuControl}
+                currentScreen="list"
+                showMarkAsRead={convo.unreadCount > 0}
+                hideTrigger={isNative}
+                triggerOpacity={
+                  !gtMobile || showActions || menuControl.isOpen ? 1 : 0
+                }
+              />
+            </View>
+          </View>
+        )}
+      </Button>
+    </View>
+  )
+}
diff --git a/src/screens/Messages/List/index.tsx b/src/screens/Messages/List/index.tsx
index b9ce8f251..05559b7d1 100644
--- a/src/screens/Messages/List/index.tsx
+++ b/src/screens/Messages/List/index.tsx
@@ -3,28 +3,22 @@ import {View} from 'react-native'
 import {ChatBskyConvoDefs} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {useNavigation} from '@react-navigation/native'
 import {NativeStackScreenProps} from '@react-navigation/native-stack'
 import {sha256} from 'js-sha256'
 
 import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
-import {MessagesTabNavigatorParams, NavigationProp} from '#/lib/routes/types'
+import {MessagesTabNavigatorParams} from '#/lib/routes/types'
 import {useGate} from '#/lib/statsig/statsig'
 import {cleanError} from '#/lib/strings/errors'
 import {logger} from '#/logger'
-import {isNative} from '#/platform/detection'
 import {useListConvos} from '#/state/queries/messages/list-converations'
-import {useSession} from '#/state/session'
 import {List} from '#/view/com/util/List'
-import {TimeElapsed} from '#/view/com/util/TimeElapsed'
-import {UserAvatar} from '#/view/com/util/UserAvatar'
 import {ViewHeader} from '#/view/com/util/ViewHeader'
 import {CenteredView} from '#/view/com/util/Views'
 import {ScrollView} from '#/view/com/util/Views'
-import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {DialogControlProps, useDialogControl} from '#/components/Dialog'
-import {ConvoMenu} from '#/components/dms/ConvoMenu'
 import {NewChat} from '#/components/dms/NewChat'
 import * as TextField from '#/components/forms/TextField'
 import {useRefreshOnFocus} from '#/components/hooks/useRefreshOnFocus'
@@ -32,10 +26,10 @@ import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus
 import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider'
 import {Link} from '#/components/Link'
 import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
-import {useMenuControl} from '#/components/Menu'
 import {Text} from '#/components/Typography'
 import {ClipClopGate} from '../gate'
 import {useDmServiceUrlStorage} from '../Temp/useDmServiceUrlStorage'
+import {ChatListItem} from './ChatListItem'
 
 type Props = NativeStackScreenProps<MessagesTabNavigatorParams, 'Messages'>
 
@@ -255,181 +249,6 @@ export function MessagesScreen({navigation, route}: Props) {
   )
 }
 
-function ChatListItem({
-  convo,
-  index,
-}: {
-  convo: ChatBskyConvoDefs.ConvoView
-  index: number
-}) {
-  const t = useTheme()
-  const {_} = useLingui()
-  const {currentAccount} = useSession()
-  const menuControl = useMenuControl()
-  const {gtMobile} = useBreakpoints()
-
-  let lastMessage = _(msg`No messages yet`)
-  let lastMessageSentAt: string | null = null
-  if (ChatBskyConvoDefs.isMessageView(convo.lastMessage)) {
-    if (convo.lastMessage.sender?.did === currentAccount?.did) {
-      lastMessage = _(msg`You: ${convo.lastMessage.text}`)
-    } else {
-      lastMessage = convo.lastMessage.text
-    }
-    lastMessageSentAt = convo.lastMessage.sentAt
-  }
-  if (ChatBskyConvoDefs.isDeletedMessageView(convo.lastMessage)) {
-    lastMessage = _(msg`Message deleted`)
-  }
-
-  const otherUser = convo.members.find(
-    member => member.did !== currentAccount?.did,
-  )
-
-  const navigation = useNavigation<NavigationProp>()
-  const [showActions, setShowActions] = React.useState(false)
-
-  const onMouseEnter = React.useCallback(() => {
-    setShowActions(true)
-  }, [])
-
-  const onMouseLeave = React.useCallback(() => {
-    setShowActions(false)
-  }, [])
-
-  const onFocus = React.useCallback<React.FocusEventHandler>(e => {
-    if (e.nativeEvent.relatedTarget == null) return
-    setShowActions(true)
-  }, [])
-
-  const onPress = React.useCallback(() => {
-    navigation.push('MessagesConversation', {
-      conversation: convo.id,
-    })
-  }, [convo.id, navigation])
-
-  if (!otherUser) {
-    return null
-  }
-
-  return (
-    <View
-      // @ts-expect-error web only
-      onMouseEnter={onMouseEnter}
-      onMouseLeave={onMouseLeave}
-      onFocus={onFocus}
-      onBlur={onMouseLeave}>
-      <Button
-        label={otherUser.displayName || otherUser.handle}
-        onPress={onPress}
-        style={a.flex_1}
-        onLongPress={isNative ? menuControl.open : undefined}>
-        {({hovered, pressed}) => (
-          <View
-            style={[
-              a.flex_row,
-              a.flex_1,
-              a.px_lg,
-              a.py_md,
-              a.gap_md,
-              (hovered || pressed) && t.atoms.bg_contrast_25,
-              index === 0 && [a.border_t, a.pt_lg],
-              t.atoms.border_contrast_low,
-            ]}>
-            <UserAvatar avatar={otherUser?.avatar} size={52} />
-            <View style={[a.flex_1, a.flex_row, a.align_center]}>
-              <View style={[a.flex_1]}>
-                <View
-                  style={[
-                    a.flex_1,
-                    a.flex_row,
-                    a.align_end,
-                    a.pb_2xs,
-                    web([{marginTop: -2}]),
-                  ]}>
-                  <Text
-                    numberOfLines={1}
-                    style={[{maxWidth: '85%'}, web([a.leading_normal])]}>
-                    <Text style={[a.text_md, t.atoms.text, a.font_bold]}>
-                      {otherUser.displayName || otherUser.handle}
-                    </Text>
-                  </Text>
-                  {lastMessageSentAt && (
-                    <TimeElapsed timestamp={lastMessageSentAt}>
-                      {({timeElapsed}) => (
-                        <Text
-                          style={[
-                            a.text_sm,
-                            web([a.leading_normal]),
-                            t.atoms.text_contrast_medium,
-                          ]}>
-                          {' '}
-                          &middot; {timeElapsed}
-                        </Text>
-                      )}
-                    </TimeElapsed>
-                  )}
-                </View>
-                <Text
-                  numberOfLines={1}
-                  style={[a.text_sm, t.atoms.text_contrast_medium, a.pb_xs]}>
-                  @{otherUser.handle}
-                </Text>
-                <Text
-                  numberOfLines={2}
-                  style={[
-                    a.text_sm,
-                    a.leading_snug,
-                    convo.unreadCount > 0
-                      ? a.font_bold
-                      : t.atoms.text_contrast_high,
-                  ]}>
-                  {lastMessage}
-                </Text>
-              </View>
-              {convo.unreadCount > 0 && (
-                <View
-                  style={[
-                    a.absolute,
-                    a.rounded_full,
-                    {
-                      backgroundColor: convo.muted
-                        ? t.palette.contrast_200
-                        : t.palette.primary_500,
-                      height: 7,
-                      width: 7,
-                    },
-                    isNative
-                      ? {
-                          top: 15,
-                          right: 12,
-                        }
-                      : {
-                          top: 0,
-                          right: 0,
-                        },
-                  ]}
-                />
-              )}
-              <ConvoMenu
-                convo={convo}
-                profile={otherUser}
-                control={menuControl}
-                currentScreen="list"
-                showMarkAsRead={convo.unreadCount > 0}
-                hideTrigger={isNative}
-                triggerOpacity={
-                  !gtMobile || showActions || menuControl.isOpen ? 1 : 0
-                }
-              />
-            </View>
-          </View>
-        )}
-      </Button>
-    </View>
-  )
-}
-
 function DesktopHeader({
   newChatControl,
   onNavigateToSettings,