fix: collaboration
deskripsi: - fix use server menjadi API - di terapkan pada main detail , room chat
This commit is contained in:
198
src/app/dev/(user)/colab/_comp/chat_window.tsx
Normal file
198
src/app/dev/(user)/colab/_comp/chat_window.tsx
Normal file
@@ -0,0 +1,198 @@
|
||||
// components/ChatWindow.tsx
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
ScrollArea,
|
||||
Text,
|
||||
TextInput
|
||||
} from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import React, { useRef, useState } from "react";
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
content: string;
|
||||
senderId: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
// components/ChatWindow.tsx
|
||||
|
||||
type Props = {
|
||||
initialMessages: Message[];
|
||||
currentUserId: string;
|
||||
loadMoreMessages: () => Promise<Message[]>;
|
||||
sendMessage: (content: string) => Promise<void>;
|
||||
};
|
||||
|
||||
const ChatWindow: React.FC<Props> = ({
|
||||
initialMessages,
|
||||
currentUserId,
|
||||
loadMoreMessages,
|
||||
sendMessage,
|
||||
}) => {
|
||||
const [messages, setMessages] = useState<Message[]>(initialMessages);
|
||||
const [newMessage, setNewMessage] = useState("");
|
||||
const scrollAreaRef = useRef<HTMLDivElement>(null);
|
||||
const messageContainerRef = useRef<HTMLDivElement>(null);
|
||||
const [showNewMessageIndicator, setShowNewMessageIndicator] = useState(false);
|
||||
const [isAtBottom, setIsAtBottom] = useState(true);
|
||||
|
||||
// Cek apakah scroll berada di bawah
|
||||
const checkIfAtBottom = () => {
|
||||
if (!scrollAreaRef.current || !messageContainerRef.current) return true;
|
||||
|
||||
const container = scrollAreaRef.current;
|
||||
const offsetBottom =
|
||||
messageContainerRef.current.clientHeight -
|
||||
container.clientHeight -
|
||||
container.scrollTop;
|
||||
|
||||
return offsetBottom < 50; // toleransi 50px
|
||||
};
|
||||
|
||||
// Scroll ke bawah
|
||||
const scrollToBottom = () => {
|
||||
if (messageContainerRef.current && scrollAreaRef.current) {
|
||||
scrollAreaRef.current.scrollTop =
|
||||
messageContainerRef.current.clientHeight -
|
||||
scrollAreaRef.current.clientHeight;
|
||||
}
|
||||
};
|
||||
|
||||
// Handle infinite scroll up
|
||||
const handleScroll = async () => {
|
||||
if (!scrollAreaRef.current) return;
|
||||
|
||||
const { scrollTop } = scrollAreaRef.current;
|
||||
|
||||
if (scrollTop === 0) {
|
||||
const newMessages = await loadMoreMessages();
|
||||
setMessages((prev) => [...newMessages, ...prev]);
|
||||
}
|
||||
|
||||
// Cek apakah user di bottom
|
||||
const atBottom = checkIfAtBottom();
|
||||
setIsAtBottom(atBottom);
|
||||
setShowNewMessageIndicator(!atBottom);
|
||||
};
|
||||
|
||||
// Kirim pesan
|
||||
const handleSendMessage = async () => {
|
||||
if (!newMessage.trim()) return;
|
||||
|
||||
// Simpan pesan baru ke state
|
||||
const tempMessage: Message = {
|
||||
id: Math.random().toString(36).substr(2, 9),
|
||||
content: newMessage,
|
||||
senderId: currentUserId,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
setMessages((prev) => [...prev, tempMessage]);
|
||||
|
||||
// Reset input
|
||||
setNewMessage("");
|
||||
|
||||
// Kirim ke server
|
||||
await sendMessage(newMessage);
|
||||
|
||||
// Jika user di bawah, scroll otomatis
|
||||
if (checkIfAtBottom()) {
|
||||
scrollToBottom();
|
||||
}
|
||||
};
|
||||
|
||||
// Scroll otomatis saat komponen pertama kali di-render
|
||||
useShallowEffect(() => {
|
||||
scrollToBottom();
|
||||
}, []);
|
||||
|
||||
// Tambah event listener scroll
|
||||
useShallowEffect(() => {
|
||||
const scrollArea = scrollAreaRef.current;
|
||||
if (!scrollArea) return;
|
||||
|
||||
scrollArea.addEventListener("scroll", handleScroll);
|
||||
return () => {
|
||||
scrollArea.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Scroll otomatis jika user di bottom dan ada pesan baru
|
||||
useShallowEffect(() => {
|
||||
if (isAtBottom) {
|
||||
scrollToBottom();
|
||||
}
|
||||
}, [messages.length]);
|
||||
|
||||
return (
|
||||
<Box h="100%" display="flex" style={{ flexDirection: "column" }}>
|
||||
{/* Area Chat */}
|
||||
<ScrollArea
|
||||
ref={scrollAreaRef}
|
||||
style={{ flex: 1, position: "relative" }}
|
||||
onScrollPositionChange={() => {}}
|
||||
>
|
||||
<div ref={messageContainerRef}>
|
||||
{messages.map((msg) => (
|
||||
<Box
|
||||
key={msg.id}
|
||||
p="sm"
|
||||
style={{
|
||||
textAlign: msg.senderId === currentUserId ? "right" : "left",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
bg={msg.senderId === currentUserId ? "blue" : "gray"}
|
||||
c="white"
|
||||
px="md"
|
||||
py="xs"
|
||||
// radius="lg"
|
||||
display="inline-block"
|
||||
>
|
||||
{msg.content}
|
||||
</Text>
|
||||
</Box>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
{/* Notifikasi Pesan Baru */}
|
||||
{!isAtBottom && showNewMessageIndicator && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
scrollToBottom();
|
||||
setShowNewMessageIndicator(false);
|
||||
}}
|
||||
color="blue"
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 80,
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
Ada pesan baru
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Input Pengiriman */}
|
||||
<Flex gap="sm" p="md" align="center">
|
||||
<TextInput
|
||||
value={newMessage}
|
||||
onChange={(e) => setNewMessage(e.currentTarget.value)}
|
||||
placeholder="Ketik pesan..."
|
||||
onKeyDown={(e) => e.key === "Enter" && handleSendMessage()}
|
||||
// flex={1}
|
||||
style={{
|
||||
flex: 1,
|
||||
}}
|
||||
/>
|
||||
<Button onClick={handleSendMessage}>Kirim</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatWindow;
|
||||
83
src/app/dev/(user)/colab/_comp/ui_chat.tsx
Normal file
83
src/app/dev/(user)/colab/_comp/ui_chat.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
// app/chat/[id]/page.tsx
|
||||
"use client";
|
||||
|
||||
import React, { useState, useCallback } from "react";
|
||||
import ChatWindow from "./chat_window";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
content: string;
|
||||
senderId: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
params: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default function ChatPage({ params }: Props) {
|
||||
const chatId = params.id;
|
||||
const currentUserId = "user_123"; // Ganti dengan dynamic user ID dari auth
|
||||
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [cursor, setCursor] = useState<string | null>(null);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
|
||||
// Load initial messages
|
||||
const loadInitialMessages = useCallback(async () => {
|
||||
const res = await fetch(`/api/messages?chatId=${chatId}`);
|
||||
const data = await res.json();
|
||||
if (data.length > 0) {
|
||||
setCursor(data[0]?.id); // Simpan cursor dari pesan pertama
|
||||
} else {
|
||||
setHasMore(false);
|
||||
}
|
||||
setMessages(data);
|
||||
}, [chatId]);
|
||||
|
||||
// Load more messages (scroll up)
|
||||
const loadMoreMessages = useCallback(async () => {
|
||||
if (!hasMore || !cursor) return [];
|
||||
|
||||
const res = await fetch(`/api/messages?chatId=${chatId}&cursor=${cursor}`);
|
||||
const data = await res.json();
|
||||
|
||||
if (data.length < 20) setHasMore(false);
|
||||
if (data.length > 0) setCursor(data[0]?.id);
|
||||
|
||||
setMessages((prev) => [...data, ...prev]);
|
||||
return data;
|
||||
}, [chatId, cursor, hasMore]);
|
||||
|
||||
// Send message
|
||||
const sendMessage = useCallback(
|
||||
async (content: string) => {
|
||||
await fetch("/api/messages", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
content,
|
||||
senderId: currentUserId,
|
||||
chatId,
|
||||
}),
|
||||
});
|
||||
},
|
||||
[chatId, currentUserId]
|
||||
);
|
||||
|
||||
// Load pesan saat pertama kali
|
||||
useShallowEffect(() => {
|
||||
loadInitialMessages();
|
||||
}, [loadInitialMessages]);
|
||||
|
||||
return (
|
||||
<ChatWindow
|
||||
initialMessages={messages}
|
||||
currentUserId={currentUserId}
|
||||
loadMoreMessages={loadMoreMessages}
|
||||
sendMessage={sendMessage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import { funGetUserIdByToken } from "@/app_modules/_global/fun/get";
|
||||
import adminColab_getOneRoomChatById from "@/app_modules/admin/colab/fun/get/get_one_room_chat_by_id";
|
||||
import { Colab_GroupChatView } from "@/app_modules/colab";
|
||||
import Colab_NewGroupChatView from "@/app_modules/colab/detail/group/new_detail_group";
|
||||
import colab_getMessageByRoomId from "@/app_modules/colab/fun/get/room_chat/get_message_by_room_id";
|
||||
import { user_getOneByUserId } from "@/app_modules/home/fun/get/get_one_user_by_id";
|
||||
import _ from "lodash";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
|
||||
export default async function Page({ params }: { params: { id: string } }) {
|
||||
const roomId = params.id;
|
||||
const userLoginId = await funGetUserIdByToken();
|
||||
// const userLoginId = await funGetUserIdByToken();
|
||||
// const dataUserLogin = await user_getOneByUserId(userLoginId as string);
|
||||
|
||||
const getData = (await adminColab_getOneRoomChatById({ roomId: roomId }))
|
||||
.data;
|
||||
@@ -18,26 +19,18 @@ export default async function Page({ params }: { params: { id: string } }) {
|
||||
"ProjectCollaboration_AnggotaRoomChat",
|
||||
]);
|
||||
|
||||
|
||||
let listMsg = await colab_getMessageByRoomId({ roomId: roomId, page: 1 });
|
||||
const dataUserLogin = await user_getOneByUserId(userLoginId as string);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <Colab_DetailGrupDiskusi
|
||||
userLoginId={userLoginId}
|
||||
listMsg={listMsg}
|
||||
selectRoom={dataRoom as any}
|
||||
dataUserLogin={dataUserLogin as any}
|
||||
roomId={roomId}
|
||||
/> */}
|
||||
|
||||
<Colab_GroupChatView
|
||||
{/* <Colab_GroupChatView
|
||||
userLoginId={userLoginId as string}
|
||||
listMsg={listMsg}
|
||||
selectRoom={dataRoom as any}
|
||||
dataUserLogin={dataUserLogin as any}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<Colab_NewGroupChatView selectRoom={dataRoom as any} listMsg={listMsg} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { funGetUserIdByToken } from "@/app_modules/_global/fun/get";
|
||||
import { Colab_MainDetail } from "@/app_modules/colab";
|
||||
|
||||
export default async function Page() {
|
||||
const userLoginId = await funGetUserIdByToken();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Colab_MainDetail
|
||||
userLoginId={userLoginId as string}
|
||||
/>
|
||||
<Colab_MainDetail />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user