Created
September 26, 2024 12:50
-
-
Save harrisrobin/b245fb43f223f36bae553509be382506 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "use client" | |
| import { GameChatMessagesListItem } from "@/app/[locale]/(dynamic)/cs/lobby/[room]/_components/game-chat-message-list-item" | |
| import { Button } from "@/components/ui/button" | |
| import { Textarea } from "@/components/ui/textarea" | |
| import type { MatchForLobby } from "@/types/database.types" | |
| import type { Message } from "@ably/chat" | |
| import { useMessages } from "@ably/chat/react" | |
| import { useUser } from "@supabase/auth-helpers-react" | |
| import type { User } from "@supabase/supabase-js" | |
| import { useCallback, useEffect, useRef, useState } from "react" | |
| import { Virtuoso, type VirtuosoHandle } from "react-virtuoso" | |
| interface GameChatProps { | |
| match: MatchForLobby | |
| initialUser: User | null | |
| } | |
| export function GameChat(props: GameChatProps) { | |
| const { initialUser } = props | |
| const [messages, setMessages] = useState<Message[]>([]) | |
| const [text, setText] = useState("") | |
| const clientUser = useUser() | |
| const user = clientUser ?? initialUser | |
| const virtuoso = useRef<VirtuosoHandle>(null) | |
| const scrollToBottom = useCallback(() => { | |
| if (virtuoso.current) { | |
| virtuoso.current.scrollToIndex({ | |
| index: messages.length - 1, | |
| }) | |
| } | |
| }, [messages.length]) | |
| useEffect(() => { | |
| setTimeout(() => { | |
| scrollToBottom() | |
| }, 100) | |
| }, [messages.length]) | |
| const { getPreviousMessages, send } = useMessages({ | |
| listener: (messageEvent) => { | |
| setMessages((prev) => [...prev, messageEvent.message]) | |
| }, | |
| onDiscontinuity: (error) => { | |
| console.log("Discontinuity detected:", error) | |
| }, | |
| }) | |
| useEffect(() => { | |
| if (getPreviousMessages) { | |
| getPreviousMessages({ limit: 50 }).then((result) => { | |
| setMessages(result.items) | |
| }) | |
| } | |
| }, [getPreviousMessages]) | |
| const publishMessage = async (text: string) => { | |
| if (!user) { | |
| return | |
| } | |
| await send({ | |
| text, | |
| metadata: { | |
| avatarUrl: user.user_metadata?.avatar_url, | |
| username: user.user_metadata?.username, | |
| userId: user.id, | |
| }, | |
| }) | |
| } | |
| const sendMessage = () => { | |
| if (text.trim()) { | |
| publishMessage(text) | |
| setText("") | |
| } | |
| } | |
| return ( | |
| <div className="flex flex-col w-full border-r border-l border-b border-t border-border rounded-lg"> | |
| <div className="flex-grow relative h-[375px] px-4"> | |
| <div className="absolute inset-x-0 top-0 h-10 bg-gradient-to-b from-background to-transparent z-10 pointer-events-none rounded-t-lg pr-6" /> | |
| <Virtuoso | |
| data={messages} | |
| style={{ | |
| display: "flex", | |
| width: "100%", | |
| height: "100%", | |
| flexDirection: "column", | |
| gap: "16px", | |
| }} | |
| ref={virtuoso} | |
| itemContent={(_, message) => ( | |
| <GameChatMessagesListItem | |
| key={message.timeserial} | |
| message={message} | |
| /> | |
| )} | |
| /> | |
| <div className="absolute inset-x-0 bottom-0 h-10 bg-gradient-to-t from-background to-transparent z-10 pointer-events-none" /> | |
| </div> | |
| <div className="flex w-full"> | |
| <div className="flex gap-2 items-center w-full border-t pr-4"> | |
| <Textarea | |
| placeholder="Type your message here." | |
| className="w-full h-30 border-none border-r resize-none h-30 p-4" | |
| value={text} | |
| onChange={(e) => setText(e.target.value)} | |
| onKeyDown={(e) => { | |
| if (e.key === "Enter") { | |
| e.preventDefault() | |
| sendMessage() | |
| } | |
| }} | |
| /> | |
| <Button onClick={sendMessage}>Send</Button> | |
| </div> | |
| </div> | |
| </div> | |
| ) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { LobbyRoomProvider } from "@/app/[locale]/(dynamic)/cs/lobby/[room]/_components/lobby-room-provider" | |
| interface LobbyLayoutProps { | |
| children: React.ReactNode | |
| params: { | |
| room: string | |
| } | |
| } | |
| export default async function LobbyLayout({ | |
| children, | |
| params, | |
| }: LobbyLayoutProps) { | |
| const { room } = params | |
| console.log("room", room) | |
| return <LobbyRoomProvider initialRoom={room}>{children}</LobbyRoomProvider> | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "use client" | |
| import { RoomOptionsDefaults } from "@ably/chat" | |
| import { ChatRoomProvider } from "@ably/chat/react" | |
| interface LobbyRoomProviderProps { | |
| children: React.ReactNode | |
| initialRoom: string | |
| } | |
| export function LobbyRoomProvider(props: LobbyRoomProviderProps) { | |
| const { children, initialRoom } = props | |
| console.log("initialRoom", initialRoom) | |
| const ablyChatRoom = `lobby:${initialRoom}` | |
| return ( | |
| <ChatRoomProvider id={ablyChatRoom} options={RoomOptionsDefaults}> | |
| {children} | |
| </ChatRoomProvider> | |
| ) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { GameChat } from "@/app/[locale]/(dynamic)/cs/lobby/[room]/_components/game-chat" | |
| import { GameInfo } from "@/app/[locale]/(dynamic)/cs/lobby/[room]/_components/game-info" | |
| import { MatchmakingLoading } from "@/app/[locale]/(dynamic)/cs/lobby/[room]/_components/matchmaking-loading" | |
| import { Button } from "@/components/ui/button" | |
| import { getAuthUser, getMatchById } from "@repo/supabase/queries" | |
| import GamePlayers from "./_components/game-players" | |
| interface ServerPageProps { | |
| params: { | |
| room: string | |
| } | |
| } | |
| export default async function Page( | |
| props: ServerPageProps, | |
| ): Promise<JSX.Element> { | |
| const { room } = props.params | |
| const matchResult = await getMatchById(room) | |
| const { data } = await getAuthUser() | |
| const user = data?.user | |
| if (!matchResult.data) { | |
| return <MatchmakingLoading /> | |
| } | |
| console.log("matchResult.data", matchResult.data) | |
| return ( | |
| <main className="flex flex-col flex-1 w-full pt-16 pb-28 gap-6 rounded-lg"> | |
| <h1 className="text-3xl font-bold uppercase">Game Lobby</h1> | |
| <div className="flex flex-col xs:flex-col sm:flex-row md:flex-row lg:flex-row xl:flex-row gap-8 justify-start"> | |
| <div className="flex flex-col justify-start sm:max-w-[350px] md:max-w-[350px] lg:max-w-[350px] xl:max-w-[350px]"> | |
| <GameInfo match={matchResult.data} /> | |
| <GamePlayers /> | |
| </div> | |
| <div className="flex w-full flex-col gap-4"> | |
| <GameChat initialUser={user} match={matchResult.data} /> | |
| <Button className="w-full uppercase" variant="default" size="lg"> | |
| Connect | |
| </Button> | |
| </div> | |
| </div> | |
| </main> | |
| ) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment