about summary refs log tree commit diff
path: root/src/components/dms
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-10-03 18:19:38 +0300
committerGitHub <noreply@github.com>2024-10-03 18:19:38 +0300
commit7e79c7f768e40ef192decfeac0dfac63c3d37468 (patch)
tree95f36a2d83f639589cbdf3adef3df239a3765c72 /src/components/dms
parent523a9a48c21ac1aade3d703703f0e59ffeac482e (diff)
downloadvoidsky-7e79c7f768e40ef192decfeac0dfac63c3d37468.tar.zst
[🐴] Reduce amount that message sent date is shown (#4228)
Diffstat (limited to 'src/components/dms')
-rw-r--r--src/components/dms/DateDivider.tsx80
-rw-r--r--src/components/dms/MessageItem.tsx178
-rw-r--r--src/components/dms/ReportDialog.tsx1
-rw-r--r--src/components/dms/util.ts9
4 files changed, 178 insertions, 90 deletions
diff --git a/src/components/dms/DateDivider.tsx b/src/components/dms/DateDivider.tsx
new file mode 100644
index 000000000..a9c82e8ea
--- /dev/null
+++ b/src/components/dms/DateDivider.tsx
@@ -0,0 +1,80 @@
+import React from 'react'
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {subDays} from 'date-fns'
+
+import {atoms as a, useTheme} from '#/alf'
+import {Text} from '../Typography'
+import {localDateString} from './util'
+
+const timeFormatter = new Intl.DateTimeFormat(undefined, {
+  hour: 'numeric',
+  minute: 'numeric',
+})
+const weekdayFormatter = new Intl.DateTimeFormat(undefined, {
+  weekday: 'long',
+})
+const longDateFormatter = new Intl.DateTimeFormat(undefined, {
+  weekday: 'short',
+  month: 'long',
+  day: 'numeric',
+})
+const longDateFormatterWithYear = new Intl.DateTimeFormat(undefined, {
+  weekday: 'short',
+  month: 'long',
+  day: 'numeric',
+  year: 'numeric',
+})
+
+let DateDivider = ({date: dateStr}: {date: string}): React.ReactNode => {
+  const {_} = useLingui()
+  const t = useTheme()
+
+  let date: string
+  const time = timeFormatter.format(new Date(dateStr))
+
+  const timestamp = new Date(dateStr)
+
+  const today = new Date()
+  const yesterday = subDays(today, 1)
+  const oneWeekAgo = subDays(today, 7)
+
+  if (localDateString(today) === localDateString(timestamp)) {
+    date = _(msg`Today`)
+  } else if (localDateString(yesterday) === localDateString(timestamp)) {
+    date = _(msg`Yesterday`)
+  } else {
+    if (timestamp < oneWeekAgo) {
+      if (timestamp.getFullYear() === today.getFullYear()) {
+        date = longDateFormatter.format(timestamp)
+      } else {
+        date = longDateFormatterWithYear.format(timestamp)
+      }
+    } else {
+      date = weekdayFormatter.format(timestamp)
+    }
+  }
+
+  return (
+    <View style={[a.w_full, a.my_lg]}>
+      <Text
+        style={[
+          a.text_xs,
+          a.text_center,
+          t.atoms.bg,
+          t.atoms.text_contrast_medium,
+          a.px_md,
+        ]}>
+        <Trans>
+          <Text style={[a.text_xs, t.atoms.text_contrast_medium, a.font_bold]}>
+            {date}
+          </Text>{' '}
+          at {time}
+        </Trans>
+      </Text>
+    </View>
+  )
+}
+DateDivider = React.memo(DateDivider)
+export {DateDivider}
diff --git a/src/components/dms/MessageItem.tsx b/src/components/dms/MessageItem.tsx
index c5c472cf0..52220e2ca 100644
--- a/src/components/dms/MessageItem.tsx
+++ b/src/components/dms/MessageItem.tsx
@@ -17,13 +17,15 @@ import {useLingui} from '@lingui/react'
 
 import {ConvoItem} from '#/state/messages/convo/types'
 import {useSession} from '#/state/session'
-import {TimeElapsed} from 'view/com/util/TimeElapsed'
+import {TimeElapsed} from '#/view/com/util/TimeElapsed'
 import {atoms as a, useTheme} from '#/alf'
 import {ActionsWrapper} from '#/components/dms/ActionsWrapper'
 import {InlineLinkText} from '#/components/Link'
 import {Text} from '#/components/Typography'
 import {isOnlyEmoji, RichText} from '../RichText'
+import {DateDivider} from './DateDivider'
 import {MessageItemEmbed} from './MessageItemEmbed'
+import {localDateString} from './util'
 
 let MessageItem = ({
   item,
@@ -33,14 +35,37 @@ let MessageItem = ({
   const t = useTheme()
   const {currentAccount} = useSession()
 
-  const {message, nextMessage} = item
+  const {message, nextMessage, prevMessage} = item
   const isPending = item.type === 'pending-message'
 
   const isFromSelf = message.sender?.did === currentAccount?.did
 
+  const nextIsMessage = ChatBskyConvoDefs.isMessageView(nextMessage)
+
   const isNextFromSelf =
-    ChatBskyConvoDefs.isMessageView(nextMessage) &&
-    nextMessage.sender?.did === currentAccount?.did
+    nextIsMessage && nextMessage.sender?.did === currentAccount?.did
+
+  const isNextFromSameSender = isNextFromSelf === isFromSelf
+
+  const isNewDay = useMemo(() => {
+    if (!prevMessage) return true
+
+    const thisDate = new Date(message.sentAt)
+    const prevDate = new Date(prevMessage.sentAt)
+
+    return localDateString(thisDate) !== localDateString(prevDate)
+  }, [message, prevMessage])
+
+  const isLastMessageOfDay = useMemo(() => {
+    if (!nextMessage || !nextIsMessage) return true
+
+    const thisDate = new Date(message.sentAt)
+    const prevDate = new Date(nextMessage.sentAt)
+
+    return localDateString(thisDate) !== localDateString(prevDate)
+  }, [message.sentAt, nextIsMessage, nextMessage])
+
+  const needsTail = isLastMessageOfDay || !isNextFromSameSender
 
   const isLastInGroup = useMemo(() => {
     // if this message is pending, it means the next message is pending too
@@ -48,24 +73,19 @@ let MessageItem = ({
       return false
     }
 
-    // if the next message is from a different sender, then it's the last in the group
-    if (isFromSelf ? !isNextFromSelf : isNextFromSelf) {
-      return true
-    }
-
-    // or, if there's a 3 minute gap between this message and the next
+    // or, if there's a 5 minute gap between this message and the next
     if (ChatBskyConvoDefs.isMessageView(nextMessage)) {
       const thisDate = new Date(message.sentAt)
       const nextDate = new Date(nextMessage.sentAt)
 
       const diff = nextDate.getTime() - thisDate.getTime()
 
-      // 3 minutes
-      return diff > 3 * 60 * 1000
+      // 5 minutes
+      return diff > 5 * 60 * 1000
     }
 
     return true
-  }, [message, nextMessage, isFromSelf, isNextFromSelf, isPending])
+  }, [message, nextMessage, isPending])
 
   const lastInGroupRef = useRef(isLastInGroup)
   if (lastInGroupRef.current !== isLastInGroup) {
@@ -80,52 +100,59 @@ let MessageItem = ({
   }, [message.text, message.facets])
 
   return (
-    <View style={[isFromSelf ? a.mr_md : a.ml_md]}>
-      <ActionsWrapper isFromSelf={isFromSelf} message={message}>
-        {AppBskyEmbedRecord.isView(message.embed) && (
-          <MessageItemEmbed embed={message.embed} />
-        )}
-        {rt.text.length > 0 && (
-          <View
-            style={
-              !isOnlyEmoji(message.text) && [
-                a.py_sm,
-                a.my_2xs,
-                a.rounded_md,
-                {
-                  paddingLeft: 14,
-                  paddingRight: 14,
-                  backgroundColor: isFromSelf
-                    ? isPending
-                      ? pendingColor
-                      : t.palette.primary_500
-                    : t.palette.contrast_50,
-                  borderRadius: 17,
-                },
-                isFromSelf ? a.self_end : a.self_start,
-                isFromSelf
-                  ? {borderBottomRightRadius: isLastInGroup ? 2 : 17}
-                  : {borderBottomLeftRadius: isLastInGroup ? 2 : 17},
-              ]
-            }>
-            <RichText
-              value={rt}
-              style={[a.text_md, isFromSelf && {color: t.palette.white}]}
-              interactiveStyle={a.underline}
-              enableTags
-              emojiMultiplier={3}
-            />
-          </View>
-        )}
-      </ActionsWrapper>
+    <>
+      {isNewDay && <DateDivider date={message.sentAt} />}
+      <View
+        style={[
+          isFromSelf ? a.mr_md : a.ml_md,
+          nextIsMessage && !isNextFromSameSender && a.mb_md,
+        ]}>
+        <ActionsWrapper isFromSelf={isFromSelf} message={message}>
+          {AppBskyEmbedRecord.isView(message.embed) && (
+            <MessageItemEmbed embed={message.embed} />
+          )}
+          {rt.text.length > 0 && (
+            <View
+              style={
+                !isOnlyEmoji(message.text) && [
+                  a.py_sm,
+                  a.my_2xs,
+                  a.rounded_md,
+                  {
+                    paddingLeft: 14,
+                    paddingRight: 14,
+                    backgroundColor: isFromSelf
+                      ? isPending
+                        ? pendingColor
+                        : t.palette.primary_500
+                      : t.palette.contrast_50,
+                    borderRadius: 17,
+                  },
+                  isFromSelf ? a.self_end : a.self_start,
+                  isFromSelf
+                    ? {borderBottomRightRadius: needsTail ? 2 : 17}
+                    : {borderBottomLeftRadius: needsTail ? 2 : 17},
+                ]
+              }>
+              <RichText
+                value={rt}
+                style={[a.text_md, isFromSelf && {color: t.palette.white}]}
+                interactiveStyle={a.underline}
+                enableTags
+                emojiMultiplier={3}
+              />
+            </View>
+          )}
+        </ActionsWrapper>
 
-      {isLastInGroup && (
-        <MessageItemMetadata
-          item={item}
-          style={isFromSelf ? a.text_right : a.text_left}
-        />
-      )}
-    </View>
+        {isLastInGroup && (
+          <MessageItemMetadata
+            item={item}
+            style={isFromSelf ? a.text_right : a.text_left}
+          />
+        )}
+      </View>
+    </>
   )
 }
 MessageItem = React.memo(MessageItem)
@@ -165,31 +192,12 @@ let MessageItemMetadata = ({
 
       const diff = now.getTime() - date.getTime()
 
-      // if under 1 minute
-      if (diff < 1000 * 60) {
+      // if under 30 seconds
+      if (diff < 1000 * 30) {
         return _(msg`Now`)
       }
 
-      // if in the last day
-      if (localDateString(now) === localDateString(date)) {
-        return time
-      }
-
-      // if yesterday
-      const yesterday = new Date(now)
-      yesterday.setDate(yesterday.getDate() - 1)
-
-      if (localDateString(yesterday) === localDateString(date)) {
-        return _(msg`Yesterday, ${time}`)
-      }
-
-      return i18n.date(date, {
-        hour: 'numeric',
-        minute: 'numeric',
-        day: 'numeric',
-        month: 'numeric',
-        year: 'numeric',
-      })
+      return time
     },
     [_],
   )
@@ -242,15 +250,5 @@ let MessageItemMetadata = ({
     </Text>
   )
 }
-
 MessageItemMetadata = React.memo(MessageItemMetadata)
 export {MessageItemMetadata}
-
-function localDateString(date: Date) {
-  // can't use toISOString because it should be in local time
-  const mm = date.getMonth()
-  const dd = date.getDate()
-  const yyyy = date.getFullYear()
-  // not padding with 0s because it's not necessary, it's just used for comparison
-  return `${yyyy}-${mm}-${dd}`
-}
diff --git a/src/components/dms/ReportDialog.tsx b/src/components/dms/ReportDialog.tsx
index 5493a1c87..2dcd77854 100644
--- a/src/components/dms/ReportDialog.tsx
+++ b/src/components/dms/ReportDialog.tsx
@@ -277,6 +277,7 @@ function PreviewMessage({message}: {message: ChatBskyConvoDefs.MessageView}) {
           message,
           key: '',
           nextMessage: null,
+          prevMessage: null,
         }}
         style={[a.text_left, a.mb_0]}
       />
diff --git a/src/components/dms/util.ts b/src/components/dms/util.ts
index 5952b9acf..003532d0c 100644
--- a/src/components/dms/util.ts
+++ b/src/components/dms/util.ts
@@ -16,3 +16,12 @@ export function canBeMessaged(profile: AppBskyActorDefs.ProfileView) {
       return false
   }
 }
+
+export function localDateString(date: Date) {
+  // can't use toISOString because it should be in local time
+  const mm = date.getMonth()
+  const dd = date.getDate()
+  const yyyy = date.getFullYear()
+  // not padding with 0s because it's not necessary, it's just used for comparison
+  return `${yyyy}-${mm}-${dd}`
+}