5. Fitur lain
Membuat Service untuk get previos chat BE
chat.dto.ts
export class PreviosMessageDto {
@IsInt()
@Type(() => Number)
conversation_id: number;
@IsInt()
@Type(() => Number)
pageSize : number;
@IsInt()
@IsOptional()
limit : number
}
chat.controller.ts
@Post('previos-message')
async previosMessage(@Body() payload: PreviosMessageDto) {
return this.chat.getPreviosMessage(payload);
}
chat.service.ts
async getPreviosMessage(
payload: PreviosMessageDto,
): Promise<ResponsePreviosMessage> {
const previosMessage = await this.messageRepository.find({
relations: ['sender', 'receiver'],
select: {
sender: { id: true },
receiver: { id: true },
},
where: {
conversation_id: {
id: payload.conversation_id,
},
},
skip: payload.limit,
take: payload.pageSize,
order: {
id: 'DESC',
},
});
return this._prevMessage('OK', payload.conversation_id, previosMessage);
}
Interasi FE
chat/interface/index.ts
export interface PreviosMessage {
conversation_id : number,
limit:number,
pageSize:number
}
chat/lib/index.ts
import { useSession } from "next-auth/react";
import useAxiosAuth from "@/hook/useAxiosAuth";
import { useMutation, useQuery } from "@tanstack/react-query";
import { PreviosMessage } from "../interface";
import useCache from "../socket/useCache";
const useConversation = () => {
const axiosAuthClient = useAxiosAuth();
const { data: session } = useSession();
const {handleAddPreMessage} = useCache()
...
const usePreviosMessage = () => {
const mutate = useMutation((payload: PreviosMessage) => {
console.log('gpatkan tiket baru', payload)
return axiosAuthClient.post("/chat/previos-message", payload);
}, {
onSuccess : (data)=> {
console.log('previos', data)
}
});
return mutate;
};
return { useGetList, useSendMessage, usePreviosMessage };
};
export default useConversation;
Merubah page.ts
chat/page.ts
"use client";
import { useEffect, useState } from "react";
import useConversation from "./lib";
import clsx from "clsx";
import { useSession } from "next-auth/react";
import useWebSocket from "@/app/admin/chat/socket/useWebSocket";
import useMessage from "@/app/admin/chat/socket/useMessage";
import InfiniteScroll from "react-infinite-scroll-component";
import { ChatMessage, UserChat, UserChatList } from "./interface";
import useEmitSocket from "./socket/useEmitSocket";
import useStoreChat from "@/store/useStoreChat";
export default function Chat() {
const { data: session } = useSession();
const { useGetList, useSendMessage, usePreviosMessage } = useConversation();
...
const [selected, setSelected] = useState<UserChat>({
totalMessages: 0,
messages: [],
limit :1,
pageSize : 10,
conversation_id : 0
});
...
const mutatePreviosMessage = usePreviosMessage();
...
return (
<>
<div
id="ticketLists"
className="h-screen-160 text-white flex flex-col-reverse overflow-auto px-5"
>
<InfiniteScroll
style={{ overflowX: "hidden", overflowY: "hidden" }}
dataLength={chatList.length} //This is important field to render the next data
next={ () => {
mutatePreviosMessage.mutate({
conversation_id : selected?.conversation_id|| 0,
limit : chatList.length,
pageSize : 10
});
}}
className="flex flex-col-reverse pb-40 space-y-5"
hasMore={selected.totalMessages > selected.messages?.length}
loader={mutatePreviosMessage.isLoading ? <p>Loding</p> : null}
endMessage={
<p style={{ textAlign: "center" }}>
<b>Yay! You have seen it all</b>
</p>
}
inverse={true}
scrollableTarget="ticketLists"
>
{[...chatList].map((x: ChatMessage, i: number) => (
<div
className={clsx(
"w-full flex mt-5", // Memastikan pesan menggunakan lebar penuh
{
"justify-end": x.sender.id === session?.user.id, // Pesan di sebelah kanan
"justify-start": x.sender.id !== session?.user.id, // Pesan di sebelah kiri
}
)}
key={i}
>
<div
className={clsx(
"px-1 py-1 rounded max-w-[75%] break-words", // Membatasi lebar pesan max 75% dan membiarkan teks membungkus
{
"bg-[#015C4B]": x.sender.id === session?.user.id, // Warna untuk pesan user sendiri
"bg-[#1D282F]": x.sender.id !== session?.user.id, // Warna untuk pesan orang lain
}
)}
>
<span className="text-sm whitespace-pre-wrap">
{x.message}
</span>
<span className="text-[10px] text-right block">
{formatDate(x.created_at)}
</span>
</div>
</div>
))}
</InfiniteScroll>
</div>
</>
);
}
ketikka kita scroll kita telah barhasil ...
Memperbahrui cache list message
chat/socket/useCache.ts
import { useQueryClient } from "@tanstack/react-query";
import { ChatMessage, Typing, UserChat, UserChatList } from "../interface";
const useCache = () => {
const queryClient = useQueryClient();
...
const handleAddPreMessage = async (data: any) => {
await queryClient.cancelQueries(["/chat/list"]);
console.log("data pre mess", data);
// Ambil data sebelumnya dari cache
const previousConversations = queryClient.getQueryData<UserChatList>([
"/chat/list",
]);
if (previousConversations) {
// Temukan apakah conversation_id sudah ada
const existingConversationIndex = previousConversations.data.findIndex(
(convo: UserChat) => convo.conversation_id === data.conversation_id
);
if (existingConversationIndex !== -1) {
// Update existing conversation
const updatedConversations = previousConversations.data.map((convo) => {
console.log('onvo.conversation_id', convo.conversation_id)
if (convo.conversation_id === data.conversation_id) {
// Tambahkan pesan baru ke array messages
const latest = convo.messages || [];
const updatedMessages = [...latest, ...data.data
];
return {
...convo,
messages: updatedMessages,
};
}
return convo;
});
console.log("updatedConversations", updatedConversations);
const sortedConversations = updatedConversations.sort((a, b) => {
const latestMessageA = a.latestMessage?.created_at
? new Date(a.latestMessage.created_at)
: new Date(0);
const latestMessageB = b.latestMessage?.created_at
? new Date(b.latestMessage.created_at)
: new Date(0);
return latestMessageB.getTime() - latestMessageA.getTime(); // Urutkan dari yang terbaru
});
// Perbarui cache dengan data yang telah diperbarui
queryClient.setQueryData(["/chat/list"], {
...previousConversations,
data: sortedConversations,
});
}
} else {
// Jika tidak ada data sebelumnya, set data baru ke cache
queryClient.setQueryData(["/chat/list"], {
status: "Success",
message: "OK",
data: [
{
id: data.conversation_id.id,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
user1: {}, // Sesuaikan dengan data yang ada
user2: {}, // Sesuaikan dengan data yang ada
messages: [data],
latestMessage: data,
conversation_id: data.conversation_id.id,
},
],
});
}
};
return { handleNewMessage, handleAddPreMessage };
};
export default useCache;
chat/lib/index.ts
import { useSession } from "next-auth/react";
import useAxiosAuth from "@/hook/useAxiosAuth";
import { useMutation, useQuery } from "@tanstack/react-query";
import { PreviosMessage } from "../interface";
import useCache from "../socket/useCache";
const useConversation = () => {
...
const {handleAddPreMessage} = useCache()
...
const usePreviosMessage = () => {
const mutate = useMutation((payload: PreviosMessage) => {
console.log('gpatkan tiket baru', payload)
return axiosAuthClient.post("/chat/previos-message", payload);
}, {
onSuccess : (data)=> {
handleAddPreMessage(data.data)
}
});
return mutate;
};
return { useGetList, useSendMessage, usePreviosMessage };
};
export default useConversation;