137 lines
4.3 KiB
TypeScript
137 lines
4.3 KiB
TypeScript
import { View, Text, Pressable } from "react-native";
|
||
import { Link } from "expo-router";
|
||
import { Ionicons } from "@expo/vector-icons";
|
||
import { useTheme } from "./theme";
|
||
import { Avatar } from "./ui/avatar";
|
||
|
||
interface ChatListItemProps {
|
||
chat: {
|
||
_id: string;
|
||
otherUser: {
|
||
_id: string;
|
||
name: string;
|
||
avatarId: string | null;
|
||
} | null;
|
||
lastMessage: {
|
||
content: string;
|
||
createdAt: number;
|
||
senderId: string;
|
||
} | null;
|
||
lastMessageAt: number;
|
||
};
|
||
currentUserId: string;
|
||
}
|
||
|
||
function formatRelativeTime(timestamp: number): string {
|
||
const now = Date.now();
|
||
const diff = now - timestamp;
|
||
const minutes = Math.floor(diff / 60000);
|
||
const hours = Math.floor(diff / 3600000);
|
||
const days = Math.floor(diff / 86400000);
|
||
|
||
if (minutes < 1) return "Сега";
|
||
if (minutes < 60) return `${minutes}м`;
|
||
if (hours < 24) return `${hours}ч`;
|
||
if (days < 7) return `${days}д`;
|
||
return new Date(timestamp).toLocaleDateString("mk-MK");
|
||
}
|
||
|
||
export function ChatListItem({ chat, currentUserId }: ChatListItemProps) {
|
||
const theme = useTheme();
|
||
|
||
const isSelfMessage = chat.lastMessage?.senderId === currentUserId;
|
||
const previewText = chat.lastMessage
|
||
? (isSelfMessage ? "Вие: " : "") +
|
||
(chat.lastMessage.content.length > 40
|
||
? chat.lastMessage.content.slice(0, 40) + "…"
|
||
: chat.lastMessage.content)
|
||
: "Нема пораки";
|
||
|
||
const isUnread = chat.lastMessage && !isSelfMessage;
|
||
|
||
return (
|
||
<Link href={`/chat/${chat._id}` as any} asChild>
|
||
<Pressable
|
||
accessible
|
||
accessibilityLabel={`Разговор со ${chat.otherUser?.name ?? "Непознат"}: ${previewText}`}
|
||
accessibilityRole="button"
|
||
style={({ pressed }) => [
|
||
{
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
paddingVertical: theme.spacing.md,
|
||
paddingHorizontal: theme.spacing.md,
|
||
gap: theme.spacing.md,
|
||
backgroundColor: theme.colors.surface,
|
||
borderBottomWidth: 1,
|
||
borderBottomColor: theme.colors.divider,
|
||
},
|
||
pressed && { backgroundColor: theme.colors.surfaceAlt },
|
||
]}
|
||
>
|
||
<View style={{ position: "relative" }}>
|
||
<Avatar
|
||
uri={chat.otherUser?.avatarId ?? null}
|
||
name={chat.otherUser?.name ?? "Непознат"}
|
||
size={52}
|
||
/>
|
||
{isUnread && (
|
||
<View
|
||
style={{
|
||
position: "absolute",
|
||
top: -1,
|
||
right: -1,
|
||
width: 12,
|
||
height: 12,
|
||
borderRadius: 6,
|
||
backgroundColor: theme.colors.primary,
|
||
borderWidth: 2.5,
|
||
borderColor: theme.colors.surface,
|
||
}}
|
||
/>
|
||
)}
|
||
</View>
|
||
<View style={{ flex: 1, gap: 3 }}>
|
||
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
|
||
<Text
|
||
style={{
|
||
...theme.typography.bodyBold,
|
||
color: theme.colors.text,
|
||
fontSize: 15,
|
||
flex: 1,
|
||
}}
|
||
numberOfLines={1}
|
||
>
|
||
{chat.otherUser?.name ?? "Непознат"}
|
||
</Text>
|
||
{chat.lastMessage && (
|
||
<Text style={{ ...theme.typography.label, color: theme.colors.textTertiary, fontSize: 11 }}>
|
||
{formatRelativeTime(chat.lastMessage.createdAt)}
|
||
</Text>
|
||
)}
|
||
</View>
|
||
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
|
||
{isUnread && (
|
||
<View style={{ width: 7, height: 7, borderRadius: 3.5, backgroundColor: theme.colors.primary }} />
|
||
)}
|
||
<Text
|
||
style={{
|
||
...theme.typography.caption,
|
||
color: isUnread ? theme.colors.text : theme.colors.textSecondary,
|
||
fontSize: 13,
|
||
flex: 1,
|
||
}}
|
||
numberOfLines={1}
|
||
>
|
||
{previewText}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
<Ionicons name="chevron-forward" size={16} color={theme.colors.textTertiary} />
|
||
</Pressable>
|
||
</Link>
|
||
);
|
||
}
|
||
|
||
export { formatRelativeTime };
|