mirror of
https://github.com/zhigang1992/react-native-chat-ui.git
synced 2026-01-12 22:50:15 +08:00
Add dates (#10)
This commit is contained in:
@@ -2,11 +2,11 @@ import { MessageType, Size, User } from '../src/types'
|
||||
|
||||
export const fileMessage: MessageType.File = {
|
||||
authorId: 'userId',
|
||||
id: 'uuidv4',
|
||||
fileName: 'flyer.pdf',
|
||||
id: 'file-uuidv4',
|
||||
mimeType: 'application/pdf',
|
||||
name: 'flyer.pdf',
|
||||
size: 15000,
|
||||
timestamp: 0,
|
||||
timestamp: 2000000,
|
||||
type: 'file',
|
||||
url: 'file:///Users/admin/flyer.pdf',
|
||||
}
|
||||
@@ -14,7 +14,9 @@ export const fileMessage: MessageType.File = {
|
||||
export const imageMessage: MessageType.Image = {
|
||||
authorId: 'userId',
|
||||
height: 100,
|
||||
id: 'uuidv4',
|
||||
id: 'image-uuidv4',
|
||||
imageName: 'imageName',
|
||||
size: 15000,
|
||||
timestamp: 0,
|
||||
type: 'image',
|
||||
url: 'https://avatars1.githubusercontent.com/u/59206044',
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"dependencies": {
|
||||
"@flyerhq/react-native-keyboard-accessory-view": "^1.5.1",
|
||||
"@flyerhq/react-native-link-preview": "^1.0.2",
|
||||
"dayjs": "^1.9.1",
|
||||
"react-native-image-viewing": "^0.2.0",
|
||||
"react-native-parsed-text": "^0.0.22"
|
||||
},
|
||||
|
||||
@@ -2,6 +2,8 @@ import {
|
||||
useComponentSize,
|
||||
usePanResponder,
|
||||
} from '@flyerhq/react-native-keyboard-accessory-view'
|
||||
import dayjs from 'dayjs'
|
||||
import calendar from 'dayjs/plugin/calendar'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
FlatList,
|
||||
@@ -10,6 +12,7 @@ import {
|
||||
StatusBar,
|
||||
StatusBarProps,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import ImageView from 'react-native-image-viewing'
|
||||
@@ -19,6 +22,8 @@ import { Input, InputAdditionalProps, InputTopLevelProps } from '../Input'
|
||||
import { Message, MessageTopLevelProps } from '../Message'
|
||||
import styles from './styles'
|
||||
|
||||
dayjs.extend(calendar)
|
||||
|
||||
export type ChatTopLevelProps = InputTopLevelProps & MessageTopLevelProps
|
||||
|
||||
export interface ChatProps extends ChatTopLevelProps {
|
||||
@@ -92,22 +97,64 @@ export const Chat = ({
|
||||
index: number
|
||||
}) => {
|
||||
const messageWidth = Math.floor(Math.min(size.width * 0.77, 440))
|
||||
const previousMessageSameAuthor =
|
||||
messages[index - 1]?.authorId === message.authorId
|
||||
// TODO: Update the logic after pagination is introduced
|
||||
const isFirst = index === 0
|
||||
const isLast = index === messages.length - 1
|
||||
const previousMessage = messages[index - 1]
|
||||
const nextMessage = messages[index + 1]
|
||||
|
||||
let nextMessageDifferentDay = false
|
||||
let nextMessageSameAuthor = false
|
||||
let previousMessageSameAuthor = false
|
||||
let previousMessageWithinTimeRange = false
|
||||
|
||||
if (!isLast) {
|
||||
nextMessageDifferentDay = !dayjs
|
||||
.unix(message.timestamp)
|
||||
.isSame(dayjs.unix(nextMessage.timestamp), 'day')
|
||||
nextMessageSameAuthor = nextMessage.authorId === message.authorId
|
||||
}
|
||||
|
||||
if (!isFirst) {
|
||||
previousMessageSameAuthor = previousMessage.authorId === message.authorId
|
||||
previousMessageWithinTimeRange =
|
||||
previousMessageSameAuthor &&
|
||||
previousMessage.timestamp - message.timestamp < 3600
|
||||
}
|
||||
|
||||
return (
|
||||
<Message
|
||||
{...{
|
||||
message,
|
||||
messageWidth,
|
||||
onFilePress,
|
||||
onImagePress: handleImagePress,
|
||||
previousMessageSameAuthor,
|
||||
renderFileMessage,
|
||||
renderImageMessage,
|
||||
renderTextMessage,
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<Message
|
||||
{...{
|
||||
message,
|
||||
messageWidth,
|
||||
onFilePress,
|
||||
onImagePress: handleImagePress,
|
||||
previousMessageSameAuthor,
|
||||
previousMessageWithinTimeRange,
|
||||
renderFileMessage,
|
||||
renderImageMessage,
|
||||
renderTextMessage,
|
||||
}}
|
||||
/>
|
||||
{(nextMessageDifferentDay || isLast) && (
|
||||
<Text
|
||||
style={StyleSheet.flatten([
|
||||
styles.dateDivider,
|
||||
{ marginTop: nextMessageSameAuthor ? 24 : 16 },
|
||||
])}
|
||||
>
|
||||
{dayjs.unix(message.timestamp).calendar(undefined, {
|
||||
sameDay: '[Today]',
|
||||
nextDay: 'DD MMMM',
|
||||
nextWeek: 'DD MMMM',
|
||||
lastDay: '[Yesterday]',
|
||||
lastWeek: 'DD MMMM',
|
||||
sameElse: 'DD MMMM',
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Chat } from '../Chat'
|
||||
describe('chat', () => {
|
||||
it('renders image preview', async () => {
|
||||
expect.assertions(1)
|
||||
const messages = [imageMessage]
|
||||
const messages = [textMessage, fileMessage, imageMessage]
|
||||
const onSendPress = jest.fn()
|
||||
const { getByRole, getByText } = render(
|
||||
<Chat messages={messages} onSendPress={onSendPress} user={user} />
|
||||
@@ -25,7 +25,7 @@ describe('chat', () => {
|
||||
|
||||
it('sends a text message', () => {
|
||||
expect.assertions(1)
|
||||
const messages = [textMessage]
|
||||
const messages = [textMessage, fileMessage, imageMessage]
|
||||
const onSendPress = jest.fn()
|
||||
const { getByLabelText } = render(
|
||||
<Chat
|
||||
@@ -42,7 +42,7 @@ describe('chat', () => {
|
||||
|
||||
it('opens file on a file message tap', () => {
|
||||
expect.assertions(1)
|
||||
const messages = [fileMessage]
|
||||
const messages = [textMessage, fileMessage, imageMessage]
|
||||
const onSendPress = jest.fn()
|
||||
const onFilePress = jest.fn()
|
||||
const { getByLabelText } = render(
|
||||
|
||||
@@ -4,8 +4,16 @@ export default StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
dateDivider: {
|
||||
color: '#1d1c21',
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
lineHeight: 16,
|
||||
marginBottom: 32,
|
||||
textAlign: 'center',
|
||||
},
|
||||
footer: {
|
||||
height: 24,
|
||||
height: 16,
|
||||
},
|
||||
list: {
|
||||
backgroundColor: '#fff',
|
||||
|
||||
@@ -31,7 +31,6 @@ export const ImageMessage = ({
|
||||
const aspectRatio = size.width / (size.height || 1)
|
||||
const isMinimized = aspectRatio < 0.1 || aspectRatio > 10
|
||||
const {
|
||||
background,
|
||||
image,
|
||||
minimizedImage,
|
||||
minimizedImageContainer,
|
||||
@@ -80,11 +79,7 @@ export const ImageMessage = ({
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<ImageBackground
|
||||
blurRadius={26}
|
||||
source={{ uri: message.url }}
|
||||
style={background}
|
||||
>
|
||||
<ImageBackground blurRadius={26} source={{ uri: message.url }} style={{}}>
|
||||
<TouchableWithoutFeedback onPress={handlePress}>
|
||||
{renderImage()}
|
||||
</TouchableWithoutFeedback>
|
||||
|
||||
@@ -13,9 +13,6 @@ const styles = ({
|
||||
user?: User
|
||||
}) =>
|
||||
StyleSheet.create({
|
||||
background: {
|
||||
flex: 1,
|
||||
},
|
||||
image: {
|
||||
aspectRatio,
|
||||
maxHeight: messageWidth,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import dayjs from 'dayjs'
|
||||
import * as React from 'react'
|
||||
import { View } from 'react-native'
|
||||
import { Text, View } from 'react-native'
|
||||
import { MessageType } from '../../types'
|
||||
import { UserContext } from '../../utils'
|
||||
import { FileMessage } from '../FileMessage'
|
||||
@@ -28,6 +29,7 @@ export interface MessageProps extends MessageTopLevelProps {
|
||||
messageWidth: number
|
||||
onImagePress: (url: string) => void
|
||||
previousMessageSameAuthor: boolean
|
||||
previousMessageWithinTimeRange: boolean
|
||||
}
|
||||
|
||||
export const Message = ({
|
||||
@@ -36,15 +38,17 @@ export const Message = ({
|
||||
onFilePress,
|
||||
onImagePress,
|
||||
previousMessageSameAuthor,
|
||||
previousMessageWithinTimeRange,
|
||||
renderFileMessage,
|
||||
renderImageMessage,
|
||||
renderTextMessage,
|
||||
}: MessageProps) => {
|
||||
const user = React.useContext(UserContext)
|
||||
const { container, contentContainer } = styles({
|
||||
const { container, contentContainer, statusContainer, time } = styles({
|
||||
message,
|
||||
messageWidth,
|
||||
previousMessageSameAuthor,
|
||||
previousMessageWithinTimeRange,
|
||||
user,
|
||||
})
|
||||
|
||||
@@ -78,6 +82,13 @@ export const Message = ({
|
||||
return (
|
||||
<View style={container}>
|
||||
<View style={contentContainer}>{renderMessage()}</View>
|
||||
{!previousMessageWithinTimeRange && (
|
||||
<View style={statusContainer}>
|
||||
<Text style={time}>
|
||||
{dayjs.unix(message.timestamp).format('h:mm a')}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,19 +5,22 @@ const styles = ({
|
||||
message,
|
||||
messageWidth,
|
||||
previousMessageSameAuthor,
|
||||
previousMessageWithinTimeRange,
|
||||
user,
|
||||
}: {
|
||||
message: MessageType.Any
|
||||
messageWidth: number
|
||||
previousMessageSameAuthor: boolean
|
||||
previousMessageWithinTimeRange: boolean
|
||||
user?: User
|
||||
}) =>
|
||||
StyleSheet.create({
|
||||
container: {
|
||||
alignSelf: user?.id === message.authorId ? 'flex-end' : 'flex-start',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: user?.id === message.authorId ? 'flex-end' : 'flex-start',
|
||||
marginBottom: previousMessageSameAuthor ? 8 : 24,
|
||||
marginBottom:
|
||||
previousMessageSameAuthor || previousMessageWithinTimeRange ? 8 : 16,
|
||||
marginHorizontal: 24,
|
||||
},
|
||||
contentContainer: {
|
||||
backgroundColor:
|
||||
@@ -27,10 +30,20 @@ const styles = ({
|
||||
borderBottomLeftRadius: user?.id === message.authorId ? 20 : 0,
|
||||
borderBottomRightRadius: user?.id === message.authorId ? 0 : 20,
|
||||
borderRadius: 20,
|
||||
marginHorizontal: 24,
|
||||
maxWidth: messageWidth,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
statusContainer: {
|
||||
alignSelf: 'flex-end',
|
||||
marginRight: 8,
|
||||
marginTop: 8,
|
||||
},
|
||||
time: {
|
||||
color: '#9e9cab',
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
lineHeight: 16,
|
||||
},
|
||||
})
|
||||
|
||||
export default styles
|
||||
|
||||
@@ -2407,6 +2407,11 @@ dayjs@^1.8.15:
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.36.tgz#be36e248467afabf8f5a86bae0de0cdceecced50"
|
||||
integrity sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw==
|
||||
|
||||
dayjs@^1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.9.1.tgz#201a755f7db5103ed6de63ba93a984141c754541"
|
||||
integrity sha512-01NCTBg8cuMJG1OQc6PR7T66+AFYiPwgDvdJmvJBn29NGzIG+DIFxPLNjHzwz3cpFIvG+NcwIjP9hSaPVoOaDg==
|
||||
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
|
||||
Reference in New Issue
Block a user