diff --git a/apps/rebreak-native/app/(app)/chat.tsx b/apps/rebreak-native/app/(app)/chat.tsx
index a257be1..6e7c45a 100644
--- a/apps/rebreak-native/app/(app)/chat.tsx
+++ b/apps/rebreak-native/app/(app)/chat.tsx
@@ -3,7 +3,6 @@ import {
View,
Text,
FlatList,
- Pressable,
TouchableOpacity,
ActivityIndicator,
Image,
@@ -45,51 +44,51 @@ function DmItem({ conv, onPress }: { conv: DmConversation; onPress: () => void }
const hasUnread = conv.unreadCount > 0;
return (
-
+
-
- {conv.partnerAvatar ? (
-
- ) : (
-
- {conv.partnerName.slice(0, 2).toUpperCase()}
-
+
+ {conv.partnerAvatar ? (
+
+ ) : (
+
+ {conv.partnerName.slice(0, 2).toUpperCase()}
+
+ )}
+
+
+
+
+ {conv.partnerName}
+
+
+ {formatTime(conv.lastMessageAt, t('chat.just_now'))}
+
+
+
+
+ {conv.isOwn ? `${t('chat.you')} ` : ''}
+ {conv.lastMessage}
+
+ {hasUnread && (
+
+
+ {conv.unreadCount > 99 ? '99+' : conv.unreadCount}
+
+
)}
-
-
-
- {conv.partnerName}
-
-
- {formatTime(conv.lastMessageAt, t('chat.just_now'))}
-
-
-
-
- {conv.isOwn ? t('chat.you') : ''}
- {conv.lastMessage}
-
- {hasUnread && (
-
- {conv.unreadCount}
-
- )}
-
-
+
-
+
);
}
@@ -352,28 +351,29 @@ function makeStyles(colors: ReturnType) {
marginTop: 12,
},
dmRow: {
- width: '100%',
flexDirection: 'row',
alignItems: 'center',
- paddingHorizontal: 14,
- paddingVertical: 11,
+ paddingHorizontal: 16,
+ paddingVertical: 12,
backgroundColor: colors.bg,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.border,
+ minHeight: 68,
},
dmAvatar: {
- width: 42,
- height: 42,
- borderRadius: 21,
+ width: 48,
+ height: 48,
+ borderRadius: 24,
backgroundColor: colors.surfaceElevated,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
- marginRight: 10,
+ marginRight: 12,
+ flexShrink: 0,
},
- dmAvatarImg: { width: 42, height: 42 },
+ dmAvatarImg: { width: 48, height: 48 },
dmAvatarInitials: {
- fontSize: 13,
+ fontSize: 15,
fontFamily: 'Nunito_700Bold',
color: colors.textMuted,
},
@@ -384,13 +384,13 @@ function makeStyles(colors: ReturnType) {
justifyContent: 'space-between',
},
dmName: {
- fontSize: 14,
+ fontSize: 15,
fontFamily: 'Nunito_700Bold',
color: colors.text,
flexShrink: 1,
marginRight: 6,
},
- dmTime: { fontSize: 11, fontFamily: 'Nunito_600SemiBold' },
+ dmTime: { fontSize: 11, fontFamily: 'Nunito_500Medium' },
dmBottomRow: {
flexDirection: 'row',
alignItems: 'center',
diff --git a/apps/rebreak-native/components/chat/ChatBubble.tsx b/apps/rebreak-native/components/chat/ChatBubble.tsx
index 2315da0..e3a7790 100644
--- a/apps/rebreak-native/components/chat/ChatBubble.tsx
+++ b/apps/rebreak-native/components/chat/ChatBubble.tsx
@@ -1,4 +1,4 @@
-import { useState, useRef } from 'react';
+import { useState } from 'react';
import {
View,
Text,
@@ -7,7 +7,6 @@ import {
Image,
StyleSheet,
Modal,
- Alert,
Platform,
} from 'react-native';
import * as Clipboard from 'expo-clipboard';
@@ -68,7 +67,6 @@ export function ChatBubble({
const colors = useColors();
const styles = makeStyles(colors);
const [actionsOpen, setActionsOpen] = useState(false);
- const longPressTimer = useRef | null>(null);
const isImageOnly =
!!msg.attachmentUrl && msg.attachmentType === 'image' && !msg.content && !msg.replyTo;
@@ -234,24 +232,25 @@ export function ChatBubble({
{msg.content}
)}
- {/* Footer */}
+ {/* Footer: timestamp + read-receipt inline below content */}
{!isImageOnly && (
-
+
{msg.likesCount > 0 && (
-
-
+
+
{msg.likesCount}
@@ -260,8 +259,9 @@ export function ChatBubble({
)}
{formatTime(msg.createdAt)}
@@ -269,9 +269,9 @@ export function ChatBubble({
{msg.isOwn && !hideReadStatus && (
)}
@@ -335,36 +335,37 @@ function makeStyles(colors: ReturnType) {
return StyleSheet.create({
row: {
flexDirection: 'row',
- paddingHorizontal: 8,
+ paddingHorizontal: 10,
},
avatarSlot: {
- width: 30,
- marginRight: 4,
+ width: 32,
+ marginRight: 6,
justifyContent: 'flex-end',
},
avatar: {
- width: 26,
- height: 26,
- borderRadius: 13,
+ width: 28,
+ height: 28,
+ borderRadius: 14,
backgroundColor: colors.surfaceElevated,
},
bubbleCol: {
- maxWidth: '78%',
+ maxWidth: '76%',
},
nickname: {
- fontSize: 10,
+ fontSize: 11,
fontFamily: 'Nunito_700Bold',
color: '#007AFF',
- marginBottom: 2,
- marginLeft: 10,
+ marginBottom: 3,
+ marginLeft: 12,
},
bubble: {
- borderRadius: 18,
- paddingHorizontal: 12,
- paddingVertical: 6,
+ borderRadius: 20,
+ paddingHorizontal: 14,
+ paddingTop: 8,
+ paddingBottom: 6,
shadowColor: '#000',
- shadowOpacity: 0.05,
- shadowRadius: 1,
+ shadowOpacity: 0.06,
+ shadowRadius: 2,
shadowOffset: { width: 0, height: 1 },
},
bubbleOwn: {
@@ -380,10 +381,10 @@ function makeStyles(colors: ReturnType) {
borderRadius: 8,
paddingHorizontal: 8,
paddingVertical: 4,
- marginBottom: 4,
+ marginBottom: 6,
},
imageWrap: {
- borderRadius: 12,
+ borderRadius: 14,
overflow: 'hidden',
position: 'relative',
},
@@ -405,27 +406,26 @@ function makeStyles(colors: ReturnType) {
},
content: {
fontSize: 14,
- lineHeight: 20,
+ lineHeight: 21,
fontFamily: 'Nunito_400Regular',
},
footer: {
- position: 'absolute',
- bottom: 4,
- right: 8,
flexDirection: 'row',
alignItems: 'center',
+ gap: 3,
+ marginTop: 4,
},
sheetBackdrop: {
flex: 1,
- backgroundColor: 'rgba(0,0,0,0.5)',
+ backgroundColor: 'rgba(0,0,0,0.35)',
justifyContent: 'flex-end',
},
sheet: {
backgroundColor: colors.bg,
- borderTopLeftRadius: 20,
- borderTopRightRadius: 20,
+ borderTopLeftRadius: 22,
+ borderTopRightRadius: 22,
padding: 8,
- paddingBottom: Platform.OS === 'ios' ? 32 : 16,
+ paddingBottom: Platform.OS === 'ios' ? 34 : 16,
},
sheetGrabber: {
width: 36,
diff --git a/apps/rebreak-native/components/chat/ChatInput.tsx b/apps/rebreak-native/components/chat/ChatInput.tsx
index 63c5e9a..ae354d6 100644
--- a/apps/rebreak-native/components/chat/ChatInput.tsx
+++ b/apps/rebreak-native/components/chat/ChatInput.tsx
@@ -240,7 +240,7 @@ function decodeBase64(base64: string): Uint8Array {
function makeStyles(colors: ReturnType) {
return StyleSheet.create({
container: {
- backgroundColor: colors.bg,
+ backgroundColor: colors.surface,
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: colors.border,
},
@@ -315,24 +315,26 @@ function makeStyles(colors: ReturnType) {
},
inputWrap: {
flex: 1,
- backgroundColor: colors.surfaceElevated,
+ backgroundColor: colors.bg,
borderRadius: 22,
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: colors.border,
paddingHorizontal: 14,
- minHeight: 36,
+ minHeight: 38,
maxHeight: 120,
justifyContent: 'center',
},
input: {
- fontSize: 14,
- lineHeight: 19,
+ fontSize: 15,
+ lineHeight: 20,
fontFamily: 'Nunito_400Regular',
color: colors.text,
- paddingVertical: Platform.OS === 'ios' ? 8 : 4,
+ paddingVertical: Platform.OS === 'ios' ? 9 : 5,
},
sendBtn: {
- width: 36,
- height: 36,
- borderRadius: 18,
+ width: 38,
+ height: 38,
+ borderRadius: 19,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 6,
diff --git a/apps/rebreak-native/components/chat/RoomCard.tsx b/apps/rebreak-native/components/chat/RoomCard.tsx
index ef7d66f..44e2e4a 100644
--- a/apps/rebreak-native/components/chat/RoomCard.tsx
+++ b/apps/rebreak-native/components/chat/RoomCard.tsx
@@ -1,4 +1,4 @@
-import { View, Text, Pressable, Image, StyleSheet } from 'react-native';
+import { View, Text, TouchableOpacity, Image, StyleSheet } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { useTranslation } from 'react-i18next';
import { useColors } from '../../lib/theme';
@@ -39,25 +39,21 @@ export function RoomCard({ room, onPress }: Props) {
.join('');
return (
-
+
-
- {room.avatarUrl ? (
-
- ) : !room.isPublic ? (
- {initials}
- ) : (
-
- )}
-
+
+ {room.avatarUrl ? (
+
+ ) : !room.isPublic ? (
+ {initials}
+ ) : (
+
+ )}
+
-
-
+
+
+
{room.name}
@@ -66,29 +62,32 @@ export function RoomCard({ room, onPress }: Props) {
Standard
)}
+
+
{room.lastMessage && (
{formatTime(room.lastMessage.createdAt, t('chat.just_now'))}
)}
-
-
- {room.lastMessage ? (
-
-
- {room.lastMessage.senderName}:{' '}
-
- {room.lastMessage.content}
-
- ) : room.description ? (
-
- {room.description}
-
- ) : null}
-
+
+
+
+
+ {room.lastMessage ? (
+
+ {room.lastMessage.senderName}:
+ {room.lastMessage.content}
+
+ ) : room.description ? (
+
+ {room.description}
+
+ ) : null}
+
+
-
+
{room.memberCount}
{!room.isMember && (
@@ -98,38 +97,40 @@ export function RoomCard({ room, onPress }: Props) {
)}
+
-
+
);
}
function makeStyles(colors: ReturnType) {
return StyleSheet.create({
row: {
- width: '100%',
flexDirection: 'row',
alignItems: 'center',
- paddingHorizontal: 14,
- paddingVertical: 11,
+ paddingHorizontal: 16,
+ paddingVertical: 12,
backgroundColor: colors.bg,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.border,
+ minHeight: 68,
},
avatar: {
- width: 42,
- height: 42,
- borderRadius: 21,
+ width: 48,
+ height: 48,
+ borderRadius: 24,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
- marginRight: 10,
+ marginRight: 12,
+ flexShrink: 0,
},
avatarImg: {
- width: 42,
- height: 42,
+ width: 48,
+ height: 48,
},
avatarInitials: {
- fontSize: 13,
+ fontSize: 15,
fontFamily: 'Nunito_700Bold',
color: colors.textMuted,
},
@@ -140,79 +141,93 @@ function makeStyles(colors: ReturnType) {
headerRow: {
flexDirection: 'row',
alignItems: 'center',
+ marginBottom: 3,
+ },
+ nameWrap: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 6,
+ minWidth: 0,
+ },
+ metaRight: {
+ marginLeft: 8,
+ flexShrink: 0,
},
footerRow: {
flexDirection: 'row',
alignItems: 'center',
- marginTop: 3,
},
footerTextWrap: {
flex: 1,
minWidth: 0,
},
- metaPill: {
+ footerRight: {
flexDirection: 'row',
alignItems: 'center',
+ gap: 6,
marginLeft: 8,
- paddingHorizontal: 6,
- paddingVertical: 2,
- borderRadius: 8,
- backgroundColor: colors.surfaceElevated,
+ flexShrink: 0,
},
name: {
- fontSize: 14,
+ fontSize: 15,
fontFamily: 'Nunito_700Bold',
color: colors.text,
flexShrink: 1,
},
defaultBadge: {
- marginLeft: 6,
paddingHorizontal: 6,
- paddingVertical: 1,
- backgroundColor: colors.surface,
- borderRadius: 8,
+ paddingVertical: 2,
+ backgroundColor: '#EFF6FF',
+ borderRadius: 6,
+ flexShrink: 0,
},
defaultBadgeText: {
fontSize: 9,
fontFamily: 'Nunito_700Bold',
- color: '#007AFF',
+ color: '#3B82F6',
},
lastMessage: {
- fontSize: 12,
+ fontSize: 13,
fontFamily: 'Nunito_400Regular',
color: colors.textMuted,
},
+ senderName: {
+ fontFamily: 'Nunito_600SemiBold',
+ color: colors.textMuted,
+ },
description: {
- fontSize: 12,
+ fontSize: 13,
fontFamily: 'Nunito_400Regular',
color: colors.textMuted,
},
- right: {
- alignItems: 'flex-end',
- marginLeft: 8,
+ metaPill: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 3,
+ paddingHorizontal: 6,
+ paddingVertical: 3,
+ borderRadius: 8,
+ backgroundColor: colors.surfaceElevated,
},
memberCount: {
fontSize: 11,
fontFamily: 'Nunito_700Bold',
color: colors.textMuted,
- marginLeft: 3,
},
time: {
- fontSize: 10,
+ fontSize: 11,
fontFamily: 'Nunito_500Medium',
color: colors.textMuted,
- marginLeft: 'auto',
- paddingLeft: 6,
},
joinBadge: {
- marginLeft: 6,
paddingHorizontal: 8,
paddingVertical: 3,
- backgroundColor: colors.surface,
+ backgroundColor: '#EFF6FF',
borderRadius: 10,
},
joinBadgeText: {
- fontSize: 10,
+ fontSize: 11,
fontFamily: 'Nunito_700Bold',
color: '#007AFF',
},