diff --git a/frontend/app/lib/actions.ts b/frontend/app/lib/actions.ts
index 88d9a3d4..f7b4fb69 100644
--- a/frontend/app/lib/actions.ts
+++ b/frontend/app/lib/actions.ts
@@ -884,3 +884,10 @@ export async function createUserWithOauth(
redirect("/login");
}
}
+
+export const isOnline = async (userId: number) =>
+ fetch(`${process.env.API_URL}/chat/${userId}/online`, {
+ headers: {
+ Authorization: "Bearer " + getAccessToken(),
+ },
+ }).then((res) => (res.ok ? res.json() : Promise.reject(res)));
diff --git a/frontend/app/pong/GameCard.tsx b/frontend/app/pong/GameCard.tsx
index 74f111ae..242cf7d4 100644
--- a/frontend/app/pong/GameCard.tsx
+++ b/frontend/app/pong/GameCard.tsx
@@ -1,18 +1,24 @@
"use client";
+import { Avatar } from "@/app/ui/user/avatar";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { useRouter } from "next/navigation";
import { PublicUserEntity } from "../lib/dtos";
-import UserTooltip from "../ui/user/user-tool-tip";
function UserCard({ user }: { user: PublicUserEntity }) {
return (
<>
>
);
diff --git a/frontend/app/room/[id]/sidebar-item.tsx b/frontend/app/room/[id]/sidebar-item.tsx
index 9f4c93e7..2b7c97c9 100644
--- a/frontend/app/room/[id]/sidebar-item.tsx
+++ b/frontend/app/room/[id]/sidebar-item.tsx
@@ -13,7 +13,7 @@ import type {
RoomEntity,
UserOnRoomEntity,
} from "@/app/lib/dtos";
-import { SmallAvatarSkeleton } from "@/app/ui/room/skeleton";
+import { Avatar } from "@/app/ui/user/avatar";
import {
ContextMenu,
ContextMenuContent,
@@ -38,13 +38,6 @@ function truncateString(str: string | undefined, num: number): string {
return str.slice(0, num) + "...";
}
-function Avatar({ avatarURL }: { avatarURL?: string }) {
- if (!avatarURL) {
- return ;
- }
- return ;
-}
-
export interface LeaveEvent {
userId: number;
roomId: number;
@@ -141,7 +134,7 @@ export default function SidebarItem({
{!isKicked && (
-
+
{truncateString(user.user.name, 15)}
{room.accessLevel !== "DIRECT" && isUserOwner && " đ"}
diff --git a/frontend/app/ui/room/message-item.tsx b/frontend/app/ui/room/message-item.tsx
index 259f3701..a379f7d4 100644
--- a/frontend/app/ui/room/message-item.tsx
+++ b/frontend/app/ui/room/message-item.tsx
@@ -2,7 +2,6 @@ import type { MessageEvent } from "@/app/lib/dtos";
import { Avatar } from "@/app/ui/user/avatar";
import { Stack } from "@/components/layout/stack";
import { TooltipProvider } from "@radix-ui/react-tooltip";
-import UserTooltip from "../user/user-tool-tip";
export function MessageItem({
message,
@@ -18,7 +17,11 @@ export function MessageItem({
{/* Left Side */}
{withAvatar && (
-
+
)}
{!withAvatar && (
diff --git a/frontend/app/ui/user/avatar.tsx b/frontend/app/ui/user/avatar.tsx
index 2e9e99e3..2e71db43 100644
--- a/frontend/app/ui/user/avatar.tsx
+++ b/frontend/app/ui/user/avatar.tsx
@@ -1,40 +1,78 @@
import { Skeleton } from "@/components/ui/skeleton";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
+import Link from "next/link";
export type AvatarSize = "small" | "medium" | "large";
-export function Avatar({
- avatarURL,
- size,
- alt,
-}: {
+export interface Props {
avatarURL?: string;
size: AvatarSize;
+ href?: string;
alt?: string;
-}) {
+ online?: boolean;
+}
+
+export function Avatar({ avatarURL, size, href, alt, online }: Props) {
let sizeClass = "";
+ let onlineStatusClass = online ? "bg-green-500 " : "bg-gray-500 ";
switch (size) {
case "small":
sizeClass = "h-6 w-6";
+ onlineStatusClass += "w-3 h-3 border-2";
break;
case "medium":
sizeClass = "h-10 w-10";
+ onlineStatusClass += "w-4 h-4 border-2";
break;
case "large":
- sizeClass = "h-32 w-32";
+ sizeClass = "h-28 w-28";
+ onlineStatusClass += "w-8 h-8 border-4";
break;
default:
- sizeClass = "h-10 w-10";
- break;
+ throw new Error("Invalid size");
}
if (!avatarURL) {
return ;
- } else {
- return (
-
- );
}
+ const TooltipWrapper = ({ children }: { children: React.ReactNode }) =>
+ alt !== undefined ? (
+
+ {children}
+ {alt}
+
+ ) : (
+ children
+ );
+ const LinkWrapper = ({ children }: { children: React.ReactNode }) =>
+ href !== undefined ? {children} : children;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {online ? "online" : "offline"}
+
+
+
+
+
+ );
}
diff --git a/frontend/app/ui/user/match-history.tsx b/frontend/app/ui/user/match-history.tsx
index 77972a4c..1eee8f11 100644
--- a/frontend/app/ui/user/match-history.tsx
+++ b/frontend/app/ui/user/match-history.tsx
@@ -1,6 +1,5 @@
import { getMatchHistory } from "@/app/lib/actions";
import type { MatchDetailEntity } from "@/app/lib/dtos";
-import Link from "next/link";
import { Avatar } from "./avatar";
import ProfileItem from "./profile-item";
@@ -17,17 +16,19 @@ function MatchDetailItem({
: "text-red-500"
: "";
return (
-
-
-
-
-
{detail.user.name}
-
- {detail.winLose} ({detail.score})
-
+
+
+
+
{detail.user.name}
+
+ {detail.winLose} ({detail.score})
-
+
);
}
@@ -37,6 +38,7 @@ export default async function MatchHistory({ userId }: { userId: number }) {
{history.map((match) => {
+ // TODO: player2 ă undefinedă§erroră«ăȘă
return (
(
+ {},
+ );
+
+ const fetchOnlineStatus = async () => {
+ try {
+ users.forEach(async (u) => {
+ const body = await isOnline(u.id);
+ const online = body.isOnline;
+ setOnlineStatus((prev) => ({ ...prev, [u.name]: online }));
+ });
+ } catch (error) {
+ console.error("Error fetching online status:", error);
+ }
+ };
+
+ useEffect(() => {
+ fetchOnlineStatus();
+ }, []);
+
return (
{users.length === 0 &&
No users to display
}
{users.map((u) => (
-
+
))}
diff --git a/frontend/app/ui/user/user-tool-tip.tsx b/frontend/app/ui/user/user-tool-tip.tsx
deleted file mode 100644
index 76f76ecd..00000000
--- a/frontend/app/ui/user/user-tool-tip.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { PublicUserEntity } from "@/app/lib/dtos";
-import {
- Tooltip,
- TooltipContent,
- TooltipTrigger,
-} from "@/components/ui/tooltip";
-import Link from "next/link";
-import { Avatar, AvatarSize } from "./avatar";
-
-// This component has to be used with TooltipProvider
-export default function UserTooltip({
- user,
- avatarSize,
-}: {
- user: PublicUserEntity;
- avatarSize: AvatarSize;
-}) {
- return (
-
-
-
-
-
-
- {user.name}
-
- );
-}