Skip to content

Commit 7a4eeb2

Browse files
committed
feat(time): add time of messages and fix typing indicator ui shaking
1 parent 1b1dc68 commit 7a4eeb2

File tree

4 files changed

+112
-68
lines changed

4 files changed

+112
-68
lines changed

app/components/ChatHistory.tsx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,27 +93,28 @@ const ChatHistory = ({
9393
>
9494
<Flex gap="8px" alignItems="center" $cursor="pointer" onClick={profileClickHandler}>
9595
<Container
96-
width="48px"
97-
height="48px"
96+
width="40px"
97+
height="40px"
9898
padding="8px"
99-
backgroundColor={isDark ? Theme.colors.black : Theme.colors.white}
100-
borderRadius="50%"
99+
backgroundColor={Theme.colors.violet}
100+
borderRadius="12px"
101+
hBackgroundColor={Theme.colors.lightViolet}
101102
cursor="pointer"
102103
>
103104
{!selectedChat.isGroupChat ? (
104105
getSenderPic(loggedUser, selectedChat.users) === defaultProfileUrl ? (
105-
<VscAccount size={32} fill={isDark ? Theme.colors.white : Theme.colors.black} />
106+
<VscAccount size={24} fill={isDark ? Theme.colors.black : Theme.colors.white} />
106107
) : (
107108
<Image
108109
src={getSenderPic(loggedUser, selectedChat.users)}
109110
alt={user?.name || "user profile photo"}
110-
width={32}
111-
height={32}
111+
width={24}
112+
height={24}
112113
style={{ borderRadius: "100%" }}
113114
/>
114115
)
115116
) : (
116-
<VscAccount size={32} fill={isDark ? Theme.colors.white : Theme.colors.black} />
117+
<VscAccount size={24} fill={isDark ? Theme.colors.black : Theme.colors.white} />
117118
)}
118119
</Container>
119120
<Flex flexDirection="column">
@@ -143,12 +144,14 @@ const ChatHistory = ({
143144
borderRadius="12px"
144145
hBackgroundColor={Theme.colors.lightViolet}
145146
cursor="pointer"
146-
onClick={() => setSelectedChat(undefined)}
147+
onClick={() => {
148+
setSelectedChat(null);
149+
}}
147150
>
148151
<BiArrowBack size={24} fill={isDark ? Theme.colors.black : Theme.colors.white} />
149152
</Container>
150153
</Flex>
151-
<SingleChat />
154+
<SingleChat fetchAgain={fetchAgain} setFetchAgain={setFetchAgain} />
152155
</>
153156
) : (
154157
<Text color={isDark ? Theme.colors.white : Theme.colors.black}>Select a chat to start a Byte Chat...</Text>

app/components/ContactList.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,27 +120,28 @@ const ContactList = ({ fetchAgain }: { fetchAgain: boolean }) => {
120120
key={userChat?._id}
121121
>
122122
<Container
123-
width="48px"
124-
height="48px"
123+
width="40px"
124+
height="40px"
125125
padding="8px"
126-
backgroundColor={isDark ? Theme.colors.black : Theme.colors.white}
127-
borderRadius="50%"
126+
backgroundColor={Theme.colors.violet}
127+
borderRadius="12px"
128+
hBackgroundColor={Theme.colors.lightViolet}
128129
cursor="pointer"
129130
>
130131
{!userChat.isGroupChat ? (
131132
getSenderPic(loggedUser, userChat.users) === defaultProfileUrl ? (
132-
<VscAccount size={32} fill={isDark ? Theme.colors.white : Theme.colors.black} />
133+
<VscAccount size={24} fill={isDark ? Theme.colors.black : Theme.colors.white} />
133134
) : (
134135
<Image
135136
src={getSenderPic(loggedUser, userChat.users)}
136137
alt={user?.name || "user profile photo"}
137-
width={32}
138-
height={32}
138+
width={24}
139+
height={24}
139140
style={{ borderRadius: "100%" }}
140141
/>
141142
)
142143
) : (
143-
<VscAccount size={32} fill={isDark ? Theme.colors.white : Theme.colors.black} />
144+
<VscAccount size={24} fill={isDark ? Theme.colors.black : Theme.colors.white} />
144145
)}
145146
</Container>
146147
<Flex flexDirection="column">

app/components/ScrollableChat.tsx

Lines changed: 62 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import Flex from "@/styles/Flex.styled";
55
import Text from "@/styles/Text.styled";
66
import Theme from "@/styles/Theme.styled";
77
import { MessageData } from "@/types";
8+
import { getTime } from "@/utils/date";
89
import Image from "next/image";
910
import React, { useContext, useEffect, useRef } from "react";
1011
import { VscAccount } from "react-icons/vsc";
11-
import { PropagateLoader } from "react-spinners";
12+
import { PulseLoader } from "react-spinners";
1213

1314
const ScrollableChat = ({ messages, isTyping }: { messages: MessageData[]; isTyping: boolean }) => {
1415
const { isDark } = useContext(DarkLightModeContext)!;
@@ -33,56 +34,67 @@ const ScrollableChat = ({ messages, isTyping }: { messages: MessageData[]; isTyp
3334
}, [messages, isTyping]);
3435

3536
return (
36-
<Flex
37-
as={"div"}
38-
$overflowY="auto"
39-
flexDirection="column"
40-
gap="12px"
41-
height="-webkit-fill-available"
42-
margin="12px 0"
43-
ref={messageContainerRef}
44-
>
45-
{messages &&
46-
messages.map((message, index) => (
47-
<Flex
48-
key={message._id}
49-
alignItems="center"
50-
gap="12px"
51-
margin="0 12px 0 0"
52-
$alignSelf={message.sender._id === user?._id ? "flex-end" : "flex-start"}
53-
>
54-
{isSameSender(messages, message, index, user?._id) || isLastMessage(messages, index, user?._id) ? (
55-
<Container
56-
width="40px"
57-
height="40px"
58-
padding="8px"
59-
backgroundColor={Theme.colors.violet}
60-
borderRadius="12px"
61-
hBackgroundColor={Theme.colors.lightViolet}
62-
cursor="pointer"
63-
>
64-
{user?.pic !== defaultProfileUrl ? (
65-
<VscAccount size={24} fill={isDark ? Theme.colors.black : Theme.colors.white} />
66-
) : (
67-
<Image
68-
src={message?.sender?.pic || ""}
69-
alt={message?.sender?.name || "user profile photo"}
70-
width={24}
71-
height={24}
72-
style={{ borderRadius: "100%" }}
73-
/>
74-
)}
75-
</Container>
76-
) : (
77-
<Container width="32px" height="32px" padding="4px" $display="hidden"></Container>
78-
)}
79-
<Text color={isDark ? Theme.colors.white : Theme.colors.black}>{message.content}</Text>
80-
</Flex>
81-
))}
82-
<Flex $alignSelf="flex-start" height="16px">
83-
{isTyping && <PropagateLoader color={Theme.colors.violet} size={10} />}
37+
<>
38+
<Flex
39+
as={"div"}
40+
$overflowY="auto"
41+
flexDirection="column"
42+
gap="12px"
43+
height="-webkit-fill-available"
44+
margin="12px 0 0"
45+
ref={messageContainerRef}
46+
>
47+
{messages &&
48+
messages.map((message, index) => (
49+
<Flex
50+
key={message._id}
51+
alignItems="center"
52+
gap="12px"
53+
margin="0 12px 0 0"
54+
$alignSelf={message.sender._id === user?._id ? "flex-end" : "flex-start"}
55+
>
56+
{isSameSender(messages, message, index, user?._id) || isLastMessage(messages, index, user?._id) ? (
57+
<Container
58+
width="40px"
59+
height="40px"
60+
padding="8px"
61+
backgroundColor={Theme.colors.violet}
62+
borderRadius="12px"
63+
hBackgroundColor={Theme.colors.lightViolet}
64+
cursor="pointer"
65+
>
66+
{user?.pic !== defaultProfileUrl ? (
67+
<VscAccount size={24} fill={isDark ? Theme.colors.black : Theme.colors.white} />
68+
) : (
69+
<Image
70+
src={message?.sender?.pic || ""}
71+
alt={message?.sender?.name || "user profile photo"}
72+
width={24}
73+
height={24}
74+
style={{ borderRadius: "100%" }}
75+
/>
76+
)}
77+
</Container>
78+
) : (
79+
<Container width="40px" height="40px" padding="4px" $display="hidden"></Container>
80+
)}
81+
<Flex gap="12px" alignItems="center">
82+
<Text fontSize="18px" color={isDark ? Theme.colors.white : Theme.colors.black}>
83+
{message.content}
84+
</Text>
85+
<Flex $alignSelf="flex-end">
86+
<Text fontSize="12px" color={isDark ? Theme.colors.lightGrey : Theme.colors.extraDarkGrey}>
87+
{getTime(message.createdAt, false)}
88+
</Text>
89+
</Flex>
90+
</Flex>
91+
</Flex>
92+
))}
8493
</Flex>
85-
</Flex>
94+
<Flex $alignSelf="flex-start" margin="6px 0" height="24px" alignItems="center">
95+
{isTyping ? <PulseLoader color={Theme.colors.violet} size={10} /> : <Container height="14px"></Container>}
96+
</Flex>
97+
</>
8698
);
8799
};
88100

app/utils/date.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export const getTime = (timestamp: string, needSeconds: boolean) => {
2+
const dateObject = new Date(timestamp);
3+
const hours = dateObject.getHours();
4+
const minutes = dateObject.getMinutes();
5+
const seconds = dateObject.getSeconds();
6+
7+
let amPm = "AM";
8+
let formattedHours = hours;
9+
10+
// Convert to 12-hour format
11+
if (hours > 13) {
12+
formattedHours = hours - 12;
13+
amPm = "PM";
14+
} else if (hours === 0) {
15+
formattedHours = 12;
16+
}
17+
18+
// Add leading zero for minutes and seconds if needed
19+
const formattedMinutes = minutes < 10 ? `0${minutes}` : minutes;
20+
const formattedSeconds = seconds < 10 ? `0${seconds}` : seconds;
21+
// Create the formatted time string
22+
let formattedTime;
23+
formattedTime = `${formattedHours}:${formattedMinutes} ${amPm}`;
24+
if (needSeconds) {
25+
formattedTime = `${formattedHours}:${formattedMinutes}:${formattedSeconds} ${amPm}`;
26+
}
27+
return formattedTime;
28+
};

0 commit comments

Comments
 (0)