import { type NextPage } from "next"; import Image from "next/image"; import Head from "next/head"; import { type GetServerSideProps } from "next"; import { useEffect, useState } from "react"; import { getServerAuthSession } from "../../server/auth"; import { useRouter } from "next/router"; import { api } from "~/utils/api"; import { z } from "zod"; import { useSession } from "next-auth/react"; import { IoCopyOutline, IoHourglassOutline, IoCheckmarkCircleOutline, IoEyeOutline, IoEyeOffOutline, IoSaveOutline, IoReloadOutline, IoDownloadOutline, } from "react-icons/io5"; import type { Prisma, Vote } from "@prisma/client"; import { configureAbly, useChannel, usePresence } from "@ably-labs/react-hooks"; import type { PresenceItem } from "~/utils/types"; import { env } from "~/env.mjs"; import Loading from "~/components/Loading"; import { FaShieldAlt } from "react-icons/fa"; import { RiVipCrownFill } from "react-icons/ri"; import Link from "next/link"; import { downloadCSV } from "~/utils/helpers"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); // Redirect to home if not logged in if (!session) { return { redirect: { destination: "/", permanent: false, }, }; } // Return session if logged in return { props: { session }, }; }; interface ExtendedVote extends Vote { id: string; userId: string; roomId: string; owner: { image: string | null; _count: Prisma.UserCountOutputType; name: string | null; email: string | null; }; value: string; } const Room: NextPage = () => { return ( <> Sprint Padawan
); }; export default Room; const RoomBody: React.FC = () => { const { data: sessionData } = useSession(); const { query } = useRouter(); const roomId = z.string().parse(query.id); const [storyNameText, setStoryNameText] = useState(""); const [roomScale, setRoomScale] = useState(""); const { data: roomFromDb, refetch: refetchRoomFromDb } = api.room.get.useQuery({ id: roomId }); const setVoteInDb = api.vote.set.useMutation({}); const setRoomInDb = api.room.set.useMutation({}); configureAbly({ key: env.NEXT_PUBLIC_ABLY_PUBLIC_KEY, clientId: sessionData?.user.id, recover: (_, cb) => { cb(true); }, }); const [channel] = useChannel( { channelName: `${env.NEXT_PUBLIC_APP_ENV}-${roomId}`, }, () => void refetchRoomFromDb() ); const [presenceData] = usePresence( `${env.NEXT_PUBLIC_APP_ENV}-${roomId}`, { name: sessionData?.user.name || "", image: sessionData?.user.image || "", client_id: sessionData?.user.id || "", role: sessionData?.user.role || "USER", } ); // Subscribe on mount and unsubscribe on unmount useEffect(() => { window.addEventListener("beforeunload", () => channel.presence.leave()); return () => { window.removeEventListener("beforeunload", () => channel.presence.leave() ); channel.presence.leave(); }; }, [channel.presence]); // Init story name useEffect(() => { if (sessionData && roomFromDb) { setStoryNameText(roomFromDb.storyName || ""); setRoomScale(roomFromDb.scale || "ERROR"); } }, [roomFromDb, sessionData]); // Helper functions const getVoteForCurrentUser = () => { if (roomFromDb && sessionData) { return ( roomFromDb && roomFromDb.votes.find((vote) => vote.userId === sessionData.user.id) ); } else { return null; } }; const setVote = (value: string) => { if (roomFromDb) { setVoteInDb.mutate({ roomId: roomFromDb.id, value: value, }); } }; const saveRoom = (visible: boolean, reset = false, log = false) => { if (roomFromDb) { setRoomInDb.mutate({ name: storyNameText, roomId: roomFromDb.id, scale: roomScale, visible: visible, reset: reset, log: log, }); } }; const downloadLogs = () => { if (roomFromDb) { // const element = document.createElement("a"); const jsonObject = roomFromDb?.logs .map((item) => { return { ...item, scale: item.scale, votes: item.votes, roomName: item.roomName, storyName: item.storyName, }; }) .concat({ id: "LATEST", createdAt: new Date(), userId: roomFromDb.owner.id, roomId: roomFromDb.id, scale: roomScale, votes: roomFromDb.votes.map((vote) => { return { name: vote.owner.name, value: vote.value, }; }), roomName: roomFromDb.roomName, storyName: storyNameText, }); downloadCSV(jsonObject, `sprint-padawan-room-${roomId}.csv`); } }; const copyRoomURLHandler = () => { navigator.clipboard .writeText(window.location.href) .then(() => { console.log(`Copied Room Link to Clipboard!`); }) .catch(() => { console.log(`Error Copying Room Link to Clipboard!`); }); }; const voteString = ( visible: boolean, votes: ExtendedVote[], presenceItem: PresenceItem ) => { const matchedVote = votes.find( (vote) => vote.userId === presenceItem.client_id ); if (visible) { if (!!matchedVote) { return

{matchedVote.value}

; } else { return ; } } else if (!!matchedVote) { return ( ); } else { return ( ); } }; // Room is loading if (roomFromDb === undefined) { return (
); // Room has been loaded } else if (roomFromDb) { return (
{roomFromDb.roomName}
ID:
{roomFromDb.id}
{roomFromDb && (

Story: {roomFromDb.storyName}

    {presenceData && presenceData .filter( (value, index, self) => index === self.findIndex( (presenceItem) => presenceItem.clientId === value.clientId ) ) .map((presenceItem) => { return (
  • {`${presenceItem.data.name}'s

    {presenceItem.data.name}{" "} {presenceItem.data.role === "ADMIN" && (

    )}{" "} {presenceItem.clientId === roomFromDb.userId && (
    )} {" : "}

    {roomFromDb && voteString( roomFromDb.visible, roomFromDb.votes, presenceItem.data )}
  • ); })}
{roomFromDb.scale.split(",").map((scaleItem, index) => { return ( ); })}
)} {sessionData && !!roomFromDb && roomFromDb.userId === sessionData.user.id && ( <>

Room Settings

setRoomScale(event.target.value)} /> setStoryNameText(event.target.value)} />
{(roomFromDb.logs.length > 0 || roomFromDb.votes.length > 0) && (
)}
)}
); // Room does not exist } else { return (

4️⃣0️⃣4️⃣

Oops! This room does not appear to exist, or may have been deleted! 😢

Back to Home
); } };