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 ( <>
{user.name} - +
); 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 ( - {alt} - ); } + const TooltipWrapper = ({ children }: { children: React.ReactNode }) => + alt !== undefined ? ( + + {children} + {alt} + + ) : ( + children + ); + const LinkWrapper = ({ children }: { children: React.ReactNode }) => + href !== undefined ? {children} : children; + return ( + +
+ + + {alt} + + + +
+
+ + {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} - - ); -}