mobileapp / src /components /chat /MessageBubble.tsx
Antaram Dev Bot
feat: complete ANTARAM.ORG ride-sharing app frontend
5c876be
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { useTheme, Surface, Text } from 'react-native-paper';
import type { Message } from '../../types/chat';
import { formatChatTimestamp } from '../../utils/dateFormat';
export interface MessageBubbleProps {
/** The message to render */
message: Message;
/** True if the message was sent by the current user */
isOwn: boolean;
/** Display name of the other user (shown on non-own messages) */
otherUserName?: string;
}
/**
* Single chat message bubble. Own messages are right-aligned with
* primaryContainer background; other messages are left-aligned with
* surfaceContainerHigh background. Rounded corners except the "tail"
* corner.
*/
export function MessageBubble({ message, isOwn, otherUserName }: MessageBubbleProps) {
const theme = useTheme();
// Handle text messages
if (message.type !== 'text') return null;
const bubbleBg = isOwn
? theme.colors.primaryContainer
: theme.colors.surfaceContainerHigh;
const textColor = isOwn
? theme.colors.onPrimaryContainer
: theme.colors.onSurface;
// Tail corner: own messages have top-right radius removed,
// other messages have top-left radius removed.
const tailRadius = 4;
const baseRadius = 16;
return (
<View
style={[
styles.wrapper,
isOwn ? styles.ownWrapper : styles.otherWrapper,
]}
>
{/* Show other user's name above their messages */}
{!isOwn && otherUserName ? (
<Text
variant="labelSmall"
style={[styles.senderName, { color: theme.colors.primary }]}
>
{otherUserName}
</Text>
) : null}
<Surface
style={[
styles.bubble,
{
backgroundColor: bubbleBg,
borderTopLeftRadius: isOwn ? baseRadius : tailRadius,
borderTopRightRadius: isOwn ? tailRadius : baseRadius,
borderBottomLeftRadius: baseRadius,
borderBottomRightRadius: baseRadius,
},
]}
elevation={0}
>
<Text
variant="bodyMedium"
style={{ color: textColor }}
>
{message.content}
</Text>
<View style={[styles.timestampRow, isOwn && styles.ownTimestampRow]}>
<Text
variant="labelSmall"
style={[styles.timestamp, { color: textColor, opacity: 0.7 }]}
>
{formatChatTimestamp(message.createdAt)}
</Text>
{isOwn && (
<Text
variant="labelSmall"
style={[styles.checkmark, { color: textColor, opacity: 0.7 }]}
>
{message.isSent ? (message.isRead ? '✓✓' : '✓') : '⏳'}
</Text>
)}
</View>
</Surface>
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
maxWidth: '80%',
marginBottom: 6,
},
ownWrapper: {
alignSelf: 'flex-end',
},
otherWrapper: {
alignSelf: 'flex-start',
},
senderName: {
marginBottom: 4,
marginLeft: 4,
},
bubble: {
paddingHorizontal: 14,
paddingTop: 10,
paddingBottom: 6,
minHeight: 36,
justifyContent: 'center',
},
timestampRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-end',
gap: 4,
marginTop: 4,
},
ownTimestampRow: {
justifyContent: 'flex-end',
},
timestamp: {
fontSize: 10,
},
checkmark: {
fontSize: 10,
},
});