Skip to content

Commit 29f0910

Browse files
committed
refonte graphique et d'information des message de la messagerie
1 parent ca430be commit 29f0910

File tree

6 files changed

+201
-32
lines changed

6 files changed

+201
-32
lines changed
Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,76 @@
1+
import type { Message as MessageType } from "../../../types/Message";
2+
import { dateOrStringToDate } from "../../../utils/dateCalculator";
13
import getProfilePictureUrl from "../../../utils/pictureProfileManager";
24

35
type MessageProps = {
4-
text: string;
5-
imageUrl: string;
6-
align?: "left" | "right";
6+
regroupement: MessageType[];
7+
userId: number;
78
};
89

9-
export default function Message({ text, imageUrl, align = "left" }: MessageProps) {
10-
const isLeft = align === "left";
10+
function heurMintute(date: Date): string {
11+
const hours = date.getHours().toString().padStart(2, "0");
12+
const minutes = date.getMinutes().toString().padStart(2, "0");
13+
return `${hours}:${minutes}`;
14+
}
15+
16+
function defClassRadius(nbMessage: number, messageNumero: number, isAutreMessage: boolean): string {
17+
// si c'est le dernier message, on arrondi en bas
18+
if (messageNumero === nbMessage)
19+
return `rounded-t-[16px] ${isAutreMessage ? "rounded-bl-[16px]" : "rounded-br-[16px]"}`;
20+
21+
// sinon, on n'arrondi pas
22+
return "rounded-[16px]";
23+
}
24+
25+
function defClassColor(isAutreMessage: boolean): string {
26+
return isAutreMessage ? "bg-[#CECFEB] border-[#A9ABE8]" : "bg-[#E6E6E6] border-[#DCDCDC]";
27+
}
28+
29+
export default function Message({ regroupement, userId }: MessageProps) {
30+
const isAutreMessage = Number(regroupement[0].user.id) === userId;
1131

1232
return (
13-
<div className={`flex items-end ${isLeft ? "flex-row" : "flex-row-reverse"}`}>
14-
<div className={`flex ${isLeft ? "flex-row" : "flex-row-reverse"}`}>
15-
{/* Avatar */}
16-
<div
17-
className={`flex ${isLeft ? "justify-start" : "justify-end"} w-[70px] h-[70px] min-aspect[1/1] rounded-full `}
18-
>
19-
<img src={getProfilePictureUrl(imageUrl)} alt="Profile" className="rounded-full object-cover" />
20-
</div>
33+
// contenaure group
34+
<div
35+
className={`w-full flex gap-[10px] items-end justify-end ${isAutreMessage ? "" : "flex-row-reverse"}`}
36+
>
37+
<div>
38+
{/* nom */}
39+
{!isAutreMessage && (
40+
<p className="capitalize text-[#4B5563] ml-[16px] mb-[2px]">
41+
{regroupement[0].user.firstName}&nbsp;
42+
<span className="uppercase">{regroupement[0].user.lastName.at(0)}.</span>
43+
</p>
44+
)}
2145

22-
{/* Message bubble TO DO: make it a component */}
23-
<div
24-
className={`max-w-xs px-4 wrap-anywhere py-2 mt-[3rem] rounded-2xl whitespace-pre-wrap ${isLeft ? "bg-grey rounded-tl-none" : "bg-light-grey rounded-tr-none"}`}
25-
>
26-
{text}
46+
<div className={`flex ${isAutreMessage ? "" : "flex-row-reverse"}`}>
47+
{/* texte */}
48+
<div className="flex flex-col gap-[10px]">
49+
{regroupement.map((message, index) => {
50+
return (
51+
<div
52+
className={`w-[320px] border-[1px] px-[16px] py-[10px] ${defClassRadius(regroupement.length, index + 1, isAutreMessage)} ${defClassColor(isAutreMessage)}`}
53+
key={message.id}
54+
>
55+
<p className="text-[16px] leading-[1.25]">{message.content}</p>
56+
<p className="text-end text-[#4B5563] text-[14px]">
57+
{heurMintute(dateOrStringToDate(message.createdAt))}
58+
</p>
59+
</div>
60+
);
61+
})}
62+
</div>
2763
</div>
2864
</div>
65+
66+
{/* pp */}
67+
<div>
68+
<img
69+
src={getProfilePictureUrl(regroupement[0].user.image_url)}
70+
alt="profile utilisateur"
71+
className="w-[60px] h-[60px] rounded-full"
72+
/>
73+
</div>
2974
</div>
3075
);
3176
}

frontend/src/components/groups/Messaging/Messaging.tsx

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import { FaArrowDown, FaLocationArrow } from "react-icons/fa";
44
import type { GetAllMessageMyGroupsQuery } from "../../../graphql/generated/graphql-types.ts";
55
import { useGetLazyMessagesLazyQuery } from "../../../graphql/generated/graphql-types.ts";
66
import type { Message as MessageType } from "../../../types/Message";
7-
import { countdownDate } from "../../../utils/dateCalculator.ts";
7+
import { countdownDate, isSameDate } from "../../../utils/dateCalculator.ts";
88
import { useMyProfileStore } from "../../../zustand/myProfileStore.ts";
99
import Icon from "../../utils/Icon.tsx";
1010
import Subtitle from "../../utils/Subtitle.tsx";
1111
import Message from "./Message.tsx";
12+
import TimeLigne from "./TimeLigne.tsx";
1213

1314
type MessagingProps = {
1415
title: string;
@@ -142,9 +143,33 @@ export default function Messaging({
142143
const orderedMessages = useMemo(() => {
143144
const sortMessages = messages.slice().reverse();
144145

145-
return sortMessages; //.reduce((acc, message) => {
146-
// return [...acc, message];
147-
// }, [] as typeof messages)
146+
const groupedMessages = sortMessages.reduce((acc, message) => {
147+
// si c'est le premier message on crée un nouveau groupe
148+
if (acc.length === 0) return [[message]];
149+
const lastGroup = acc[acc.length - 1];
150+
151+
// si le user est le meme que le group précédent
152+
if (message.user.id === lastGroup[0].user.id) {
153+
// si il y a moins de 5 messages dans le groupe on ajoute le message au groupe
154+
if (lastGroup.length < 5) {
155+
// si le message a été crée a maxiume 10minutes du message précédent on ajoute le message au groupe
156+
const lastMessageDate = new Date(lastGroup[lastGroup.length - 1].createdAt).getTime();
157+
const messageDate = new Date(message.createdAt).getTime();
158+
159+
if (messageDate - lastMessageDate < 10 * 60 * 1000) {
160+
acc[acc.length - 1] = [...lastGroup, message];
161+
return acc;
162+
}
163+
}
164+
}
165+
166+
acc.push([message]);
167+
// ajouter un nouveau groupe si l'utilisateur est différent ou si le regroupement est plein
168+
return acc;
169+
}, [] as MessageType[][]);
170+
171+
// reture du useMemo
172+
return groupedMessages;
148173
}, [messages]);
149174

150175
useEffect(() => {
@@ -194,17 +219,18 @@ export default function Messaging({
194219
<div className="relative w-full overflow-hidden min-h-auto flex-grow flex-shrink basis-0 pt-[10px] pb-2.5 pl-0">
195220
<div
196221
ref={contenairMessageRef}
197-
className="w-full overflow-y-auto min-h-auto h-full pr-[10px]"
222+
className="flex flex-col w-full overflow-y-auto min-h-auto h-full pr-[10px] gap-[20px]"
198223
onScroll={onScrollContainerMessages}
199224
>
200-
{orderedMessages.map((message) => {
225+
{orderedMessages.map((message, index) => {
201226
return (
202-
<Message
203-
key={message.id}
204-
text={message.content}
205-
imageUrl={message.user.image_url ? message.user.image_url : ""}
206-
align={message.user.id === userProfile?.id ? "right" : "left"}
207-
/>
227+
<div key={message[0].id} className="flex flex-col">
228+
{(index === 0 ||
229+
!isSameDate(message[0].createdAt, orderedMessages[index - 1][0].createdAt)) && (
230+
<TimeLigne date={message[0].createdAt} />
231+
)}
232+
<Message regroupement={message} userId={userProfile ? Number(userProfile.id) : 0} />
233+
</div>
208234
);
209235
})}
210236
<div ref={bottomRef} />
@@ -220,7 +246,8 @@ export default function Messaging({
220246
});
221247
}}
222248
>
223-
{getNbNewMessages(groupId, messages)} nouveaux message
249+
{getNbNewMessages(groupId, messages)} nouveau{getNbNewMessages(groupId, messages) > 1 && "x"}{" "}
250+
message{getNbNewMessages(groupId, messages) > 1 && "s"}
224251
<FaArrowDown />
225252
</button>
226253
)}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { dateOrStringToDate, isSameDate } from "../../../utils/dateCalculator";
2+
3+
type TimeLigneProps = {
4+
date: Date | string;
5+
};
6+
7+
function getJourWeek(date: Date): string {
8+
const jours = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"];
9+
return jours[date.getDay()];
10+
}
11+
12+
function getMois(date: Date): string {
13+
const mois = [
14+
"Janvier",
15+
"Février",
16+
"Mars",
17+
"Avril",
18+
"Mai",
19+
"Juin",
20+
"Juillet",
21+
"Août",
22+
"Septembre",
23+
"Octobre",
24+
"Novembre",
25+
"Décembre",
26+
];
27+
return mois[date.getMonth()];
28+
}
29+
30+
const TimeLigne = ({ date }: TimeLigneProps) => {
31+
const dateObj = dateOrStringToDate(date);
32+
33+
function dateText(date: Date): string {
34+
const today = new Date();
35+
// si c'est haujourd'hui, on affiche "Aujourd'hui"
36+
if (isSameDate(date, today)) {
37+
return "Aujourd'hui";
38+
}
39+
// si c'est hier, on affiche "Hier"
40+
const yesterday = new Date(today);
41+
yesterday.setDate(today.getDate() - 1);
42+
if (isSameDate(date, yesterday)) {
43+
return "Hier";
44+
}
45+
46+
// si c'est dans la même semaine, on affiche le jour de la semaine
47+
const weekMax = new Date(today);
48+
weekMax.setDate(today.getDate() - 7);
49+
50+
if (date.getTime() > weekMax.getTime()) {
51+
return getJourWeek(date);
52+
}
53+
54+
// si c'est dans la même année, on affiche le jour et le mois
55+
if (date.getFullYear() === today.getFullYear()) {
56+
return `${getJourWeek(date)} ${date.getDate()} ${getMois(date)}`;
57+
}
58+
59+
// sion on affiche le jour, le mois et l'année
60+
return `${getJourWeek(date)} ${date.getDate()} ${getMois(date)} ${date.getFullYear()}`;
61+
}
62+
return (
63+
<div className="flex items-center justify-center my-[8px]">
64+
<p className="">{dateText(dateObj)}</p>
65+
</div>
66+
);
67+
};
68+
69+
export default TimeLigne;

frontend/src/types/Message.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
import type { GetAllMessageMyGroupsQuery } from "../graphql/generated/graphql-types";
2+
13
export type Message = GetAllMessageMyGroupsQuery["getAllMessageMyGroups"][number]["messages"][number];

frontend/src/utils/dateCalculator.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,29 @@ export function countdownDate(date: Date) {
1515
export function formatDate(date: Date) {
1616
return date.toLocaleDateString("fr-FR", { day: "2-digit", month: "2-digit", year: "numeric" });
1717
}
18+
19+
/**
20+
*
21+
* @param date date ou strigne a convertir en date
22+
* @returns une date forcément au format Date, même si c'est une string
23+
*/
24+
export function dateOrStringToDate(date: Date | string): Date {
25+
return typeof date === "string" ? new Date(date) : date;
26+
}
27+
28+
/**
29+
* return true si les 2 dates sont le même jour (même année, même mois, même jour) mais pas forcément la même heure
30+
* @param date1
31+
* @param date2
32+
* @returns
33+
*/
34+
export function isSameDate(date1: Date | string, date2: Date | string): boolean {
35+
date1 = dateOrStringToDate(date1);
36+
date2 = dateOrStringToDate(date2);
37+
38+
return (
39+
date1.getFullYear() === date2.getFullYear() &&
40+
date1.getMonth() === date2.getMonth() &&
41+
date1.getDate() === date2.getDate()
42+
);
43+
}

frontend/src/utils/pictureProfileManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export default function getProfilePictureUrl(imageUrl: string | null): string {
1+
export default function getProfilePictureUrl(imageUrl: string | null | undefined): string {
22
if (!imageUrl) {
33
return "/images/papier-theme.jpg";
44
}

0 commit comments

Comments
 (0)