rm old messageitems
This commit is contained in:
@@ -1,82 +0,0 @@
|
||||
import {memo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
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_semi_bold]}>
|
||||
{date}
|
||||
</Text>{' '}
|
||||
at {time}
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
DateDivider = memo(DateDivider)
|
||||
export {DateDivider}
|
||||
@@ -1,49 +0,0 @@
|
||||
import {memo} from 'react'
|
||||
import {useWindowDimensions, View} from 'react-native'
|
||||
import {type $Typed, type AppBskyEmbedRecord} from '@atproto/api'
|
||||
|
||||
import {atoms as a, native, tokens, useTheme, web} from '#/alf'
|
||||
import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
|
||||
import {MessageContextProvider} from './MessageContext'
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
let MessageItemEmbed = ({
|
||||
embed,
|
||||
}: {
|
||||
embed: $Typed<AppBskyEmbedRecord.View>
|
||||
}): React.ReactNode => {
|
||||
const t = useTheme()
|
||||
const screen = useWindowDimensions()
|
||||
|
||||
return (
|
||||
<MessageContextProvider>
|
||||
<View
|
||||
style={[
|
||||
a.my_xs,
|
||||
t.atoms.bg,
|
||||
a.rounded_md,
|
||||
native({
|
||||
flexBasis: 0,
|
||||
width: Math.min(screen.width, 600) / 1.4,
|
||||
}),
|
||||
web({
|
||||
width: '100%',
|
||||
minWidth: 280,
|
||||
maxWidth: 360,
|
||||
}),
|
||||
]}>
|
||||
<View style={{marginTop: tokens.space.sm * -1}}>
|
||||
<Embed
|
||||
embed={embed}
|
||||
allowNestedQuotes
|
||||
viewContext={PostEmbedViewContext.Feed}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</MessageContextProvider>
|
||||
)
|
||||
}
|
||||
MessageItemEmbed = memo(MessageItemEmbed)
|
||||
export {MessageItemEmbed}
|
||||
@@ -1,335 +0,0 @@
|
||||
import {memo, useCallback, useMemo} from 'react'
|
||||
import {
|
||||
type GestureResponderEvent,
|
||||
type StyleProp,
|
||||
type TextStyle,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import Animated, {
|
||||
LayoutAnimationConfig,
|
||||
LinearTransition,
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
} from 'react-native-reanimated'
|
||||
import {
|
||||
AppBskyEmbedRecord,
|
||||
ChatBskyConvoDefs,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type I18n} from '@lingui/core'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {useConvoActive} from '#/state/messages/convo'
|
||||
import {type ConvoItem} from '#/state/messages/convo/types'
|
||||
import {useSession} from '#/state/session'
|
||||
import {TimeElapsed} from '#/view/com/util/TimeElapsed'
|
||||
import {atoms as a, native, useTheme} from '#/alf'
|
||||
import {isOnlyEmoji} from '#/alf/typography'
|
||||
import {ActionsWrapper} from '#/components/dms/ActionsWrapper'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
import {DateDivider as DateDividerDeprecated} from './DateDivider_DEPRECATED'
|
||||
import {MessageItemEmbed as MessageItemEmbedDeprecated} from './MessageItemEmbed_DEPRECATED'
|
||||
import {localDateString} from './util'
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
let MessageItem = ({
|
||||
item,
|
||||
}: {
|
||||
item: ConvoItem & {type: 'message' | 'pending-message'}
|
||||
}): React.ReactNode => {
|
||||
const t = useTheme()
|
||||
const {currentAccount} = useSession()
|
||||
const {_} = useLingui()
|
||||
const {convo} = useConvoActive()
|
||||
|
||||
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 =
|
||||
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
|
||||
if (isPending && nextMessage) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
// 5 minutes
|
||||
return diff > 5 * 60 * 1000
|
||||
}
|
||||
|
||||
return true
|
||||
}, [message, nextMessage, isPending])
|
||||
|
||||
const pendingColor = t.palette.primary_200
|
||||
|
||||
const rt = useMemo(() => {
|
||||
return new RichTextAPI({text: message.text, facets: message.facets})
|
||||
}, [message.text, message.facets])
|
||||
|
||||
const appliedReactions = (
|
||||
<LayoutAnimationConfig skipEntering skipExiting>
|
||||
{message.reactions && message.reactions.length > 0 && (
|
||||
<View
|
||||
style={[isFromSelf ? a.align_end : a.align_start, a.px_sm, a.pb_2xs]}>
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.gap_2xs,
|
||||
a.py_xs,
|
||||
a.px_xs,
|
||||
a.justify_center,
|
||||
isFromSelf ? a.justify_end : a.justify_start,
|
||||
a.flex_wrap,
|
||||
a.pb_xs,
|
||||
t.atoms.bg_contrast_25,
|
||||
a.border,
|
||||
t.atoms.border_contrast_low,
|
||||
a.rounded_lg,
|
||||
t.atoms.shadow_sm,
|
||||
{
|
||||
// vibe coded number
|
||||
transform: [{translateY: -11}],
|
||||
},
|
||||
]}>
|
||||
{message.reactions.map((reaction, _i, reactions) => {
|
||||
let label
|
||||
if (reaction.sender.did === currentAccount?.did) {
|
||||
label = _(msg`You reacted ${reaction.value}`)
|
||||
} else {
|
||||
const senderDid = reaction.sender.did
|
||||
const sender = convo.members.find(
|
||||
member => member.did === senderDid,
|
||||
)
|
||||
if (sender) {
|
||||
label = _(
|
||||
msg`${sanitizeDisplayName(
|
||||
sender.displayName || sender.handle,
|
||||
)} reacted ${reaction.value}`,
|
||||
)
|
||||
} else {
|
||||
label = _(msg`Someone reacted ${reaction.value}`)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Animated.View
|
||||
entering={native(ZoomIn.springify(200).delay(400))}
|
||||
exiting={reactions.length > 1 && native(ZoomOut.delay(200))}
|
||||
layout={native(LinearTransition.delay(300))}
|
||||
key={reaction.sender.did + reaction.value}
|
||||
style={[a.p_2xs]}
|
||||
accessible={true}
|
||||
accessibilityLabel={label}
|
||||
accessibilityHint={_(
|
||||
msg`Double tap or long press the message to add a reaction`,
|
||||
)}>
|
||||
<Text emoji style={[a.text_sm]}>
|
||||
{reaction.value}
|
||||
</Text>
|
||||
</Animated.View>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</LayoutAnimationConfig>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{isNewDay && <DateDividerDeprecated 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) && (
|
||||
<MessageItemEmbedDeprecated 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}
|
||||
shouldProxyLinks={true}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{IS_NATIVE && appliedReactions}
|
||||
</ActionsWrapper>
|
||||
|
||||
{!IS_NATIVE && appliedReactions}
|
||||
|
||||
{isLastInGroup && (
|
||||
<MessageItemMetadata
|
||||
item={item}
|
||||
style={isFromSelf ? a.text_right : a.text_left}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
MessageItem = memo(MessageItem)
|
||||
export {MessageItem}
|
||||
|
||||
let MessageItemMetadata = ({
|
||||
item,
|
||||
style,
|
||||
}: {
|
||||
item: ConvoItem & {type: 'message' | 'pending-message'}
|
||||
style: StyleProp<TextStyle>
|
||||
}): React.ReactNode => {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {message} = item
|
||||
|
||||
const handleRetry = useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
if (item.type === 'pending-message' && item.retry) {
|
||||
e.preventDefault()
|
||||
item.retry()
|
||||
return false
|
||||
}
|
||||
},
|
||||
[item],
|
||||
)
|
||||
|
||||
const relativeTimestamp = useCallback(
|
||||
(i18n: I18n, timestamp: string) => {
|
||||
const date = new Date(timestamp)
|
||||
const now = new Date()
|
||||
|
||||
const time = i18n.date(date, {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
})
|
||||
|
||||
const diff = now.getTime() - date.getTime()
|
||||
|
||||
// if under 30 seconds
|
||||
if (diff < 1000 * 30) {
|
||||
return _(msg`Now`)
|
||||
}
|
||||
|
||||
return time
|
||||
},
|
||||
[_],
|
||||
)
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.mt_2xs,
|
||||
a.mb_lg,
|
||||
t.atoms.text_contrast_medium,
|
||||
style,
|
||||
]}>
|
||||
<TimeElapsed timestamp={message.sentAt} timeToString={relativeTimestamp}>
|
||||
{({timeElapsed}) => (
|
||||
<Text style={[a.text_xs, t.atoms.text_contrast_medium]}>
|
||||
{timeElapsed}
|
||||
</Text>
|
||||
)}
|
||||
</TimeElapsed>
|
||||
|
||||
{item.type === 'pending-message' && item.failed && (
|
||||
<>
|
||||
{' '}
|
||||
·{' '}
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
{
|
||||
color: t.palette.negative_400,
|
||||
},
|
||||
]}>
|
||||
{_(msg`Failed to send`)}
|
||||
</Text>
|
||||
{item.retry && (
|
||||
<>
|
||||
{' '}
|
||||
·{' '}
|
||||
<InlineLinkText
|
||||
label={_(msg`Click to retry failed message`)}
|
||||
to="#"
|
||||
onPress={handleRetry}
|
||||
style={[a.text_xs]}>
|
||||
{_(msg`Retry`)}
|
||||
</InlineLinkText>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
MessageItemMetadata = memo(MessageItemMetadata)
|
||||
export {MessageItemMetadata}
|
||||
@@ -1,64 +0,0 @@
|
||||
import {useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {type ConvoItem, ConvoItemError} from '#/state/messages/convo/types'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export function MessageListError({item}: {item: ConvoItem & {type: 'error'}}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {description, help, cta} = useMemo(() => {
|
||||
return {
|
||||
[ConvoItemError.FirehoseFailed]: {
|
||||
description: _(msg`This chat was disconnected`),
|
||||
help: _(msg`Press to attempt reconnection`),
|
||||
cta: _(msg`Reconnect`),
|
||||
},
|
||||
[ConvoItemError.HistoryFailed]: {
|
||||
description: _(msg`Failed to load past messages`),
|
||||
help: _(msg`Press to retry`),
|
||||
cta: _(msg`Retry`),
|
||||
},
|
||||
}[item.code]
|
||||
}, [_, item.code])
|
||||
|
||||
return (
|
||||
<View style={[a.py_md, a.w_full, a.flex_row, a.justify_center]}>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
a.gap_sm,
|
||||
{maxWidth: 400},
|
||||
]}>
|
||||
<CircleInfo size="sm" fill={t.palette.negative_400} />
|
||||
|
||||
<Text style={[a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||
{description} ·{' '}
|
||||
{item.retry && (
|
||||
<InlineLinkText
|
||||
to="#"
|
||||
label={help}
|
||||
onPress={e => {
|
||||
e.preventDefault()
|
||||
item.retry?.()
|
||||
return false
|
||||
}}>
|
||||
{cta}
|
||||
</InlineLinkText>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -51,11 +51,9 @@ import {ChatDisabled} from '#/screens/Messages/components/ChatDisabled'
|
||||
import {MessageComposer} from '#/screens/Messages/components/MessageComposer'
|
||||
import {MessageInput} from '#/screens/Messages/components/MessageInput'
|
||||
import {MessageListError} from '#/screens/Messages/components/MessageListError'
|
||||
import {MessageListError as MessageListErrorDeprecated} from '#/screens/Messages/components/MessageListError_DEPRECATED'
|
||||
import {atoms as a, platform, tokens, useTheme, web} from '#/alf'
|
||||
import {ChatEmptyPill} from '#/components/dms/ChatEmptyPill'
|
||||
import {MessageItem} from '#/components/dms/MessageItem'
|
||||
import {MessageItem as MessageItemDeprecated} from '#/components/dms/MessageItem_DEPRECATED'
|
||||
import {NewMessagesPill} from '#/components/dms/NewMessagesPill'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
@@ -107,8 +105,6 @@ export function MessagesList({
|
||||
const {embedUri, setEmbed} = useMessageEmbed()
|
||||
const t = useTheme()
|
||||
|
||||
const isGroupChatEnabled = ax.features.enabled(ax.features.GroupChatsEnable)
|
||||
|
||||
const textInputId = 'chat-input-' + useId()
|
||||
const flatListRef = useAnimatedRef<ListMethods>()
|
||||
|
||||
@@ -363,7 +359,7 @@ export function MessagesList({
|
||||
|
||||
const renderItem = ({item}: {item: ConvoItem}) => {
|
||||
if (item.type === 'message' || item.type === 'pending-message') {
|
||||
return isGroupChatEnabled ? (
|
||||
return (
|
||||
<MessageItem
|
||||
item={item}
|
||||
profile={convoState.convo.members.find(
|
||||
@@ -371,17 +367,11 @@ export function MessagesList({
|
||||
)}
|
||||
isGroupChat={convoState.convo.kind === 'group'}
|
||||
/>
|
||||
) : (
|
||||
<MessageItemDeprecated item={item} />
|
||||
)
|
||||
} else if (item.type === 'deleted-message') {
|
||||
return <Text>Deleted message</Text>
|
||||
} else if (item.type === 'error') {
|
||||
return isGroupChatEnabled ? (
|
||||
<MessageListError item={item} />
|
||||
) : (
|
||||
<MessageListErrorDeprecated item={item} />
|
||||
)
|
||||
return <MessageListError item={item} />
|
||||
}
|
||||
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user