import { getAuth } from "@clerk/remix/ssr.server"; import { LoaderFunction, redirect } from "@remix-run/node"; import { Link, useParams } from "@remix-run/react"; import { CheckCircleIcon, CopyIcon, CrownIcon, DownloadIcon, EyeIcon, EyeOffIcon, HourglassIcon, RefreshCwIcon, SaveIcon, ShieldIcon, StarIcon, } from "lucide-react"; import { useEffect, useState } from "react"; import LoadingIndicator from "~/components/LoadingIndicator"; import { useEventSource } from "remix-utils/sse/react"; import { PresenceItem, RoomResponse, VoteResponse } from "~/services/types"; import { isAdmin, jsonToCsv } from "~/services/helpers"; import { useUser } from "@clerk/remix"; import { db } from "~/services/db.server"; import { rooms } from "~/services/schema"; import { eq } from "drizzle-orm"; import { shitList } from "~/services/consts"; // Loader export const loader: LoaderFunction = async (args) => { const { userId, sessionClaims } = await getAuth(args); if (!userId) { return redirect("/sign-in"); } const room = await db.query.rooms.findFirst({ where: eq(rooms.id, args.params.roomId as string), }); if (!room) { throw new Response(null, { status: 404, statusText: "Not Found", }); } let isShit = false; const email = sessionClaims.email as string; shitList.forEach((shitItem) => { if (email.includes(shitItem)) { isShit = true; } }); if (isShit) { return redirect( "https://www.youtube.com/watch?v=dQw4w9WgXcQ&pp=ygUXbmV2ZXIgZ29ubmEgZ2l2ZSB5b3UgdXA%3D" ); } return {}; }; // Checks for 404 export function ErrorBoundary() { return ( 4️⃣0️⃣4️⃣ Oops! This room does not appear to exist, or may have been deleted! 😢 Back to Home ); } export default function Room() { const { user } = useUser(); const params = useParams(); const roomId = params.roomId; let roomFromDb = useEventSource(`/api/room/get/${roomId}`, { event: `room-${params.roomId}`, }); let votesFromDb = useEventSource(`/api/votes/get/${roomId}`, { event: `votes-${params.roomId}`, }); let presenceData = useEventSource(`/api/room/presence/get/${roomId}`, { event: `${user?.id}-${params.roomId}`, }); let roomFromDbParsed = (roomFromDb ? JSON.parse(roomFromDb!) : undefined) as | RoomResponse | null | undefined; let votesFromDbParsed = JSON.parse(votesFromDb!) as VoteResponse | undefined; let presenceDateParsed = JSON.parse(presenceData!) as | PresenceItem[] | undefined; const [storyNameText, setStoryNameText] = useState(""); const [roomScale, setRoomScale] = useState(""); const [copied, setCopied] = useState(false); // Handlers // ================================= async function setVoteHandler(value: string) { if (roomFromDb) { await fetch(`/api/vote/set/${roomId}`, { cache: "no-cache", method: "PUT", body: JSON.stringify({ value, }), }); } } async function setRoomHandler(data: { visible: boolean; reset: boolean | undefined; log: boolean | undefined; }) { if (roomFromDb) { await fetch(`/api/room/set/${roomId}`, { cache: "no-cache", method: "PUT", body: JSON.stringify({ name: storyNameText, visible: data.visible, scale: roomScale, reset: data.reset ? data.reset : false, log: data.log ? data.log : false, }), }); } } // Helpers // ================================= const getVoteForCurrentUser = () => { if (roomFromDb) { return ( votesFromDbParsed && votesFromDbParsed.find((vote) => vote.userId === user?.id) ); } else { return null; } }; const downloadLogs = () => { if (roomFromDbParsed && votesFromDbParsed) { const jsonObject = roomFromDbParsed?.logs .map((item) => { return { id: item.id, created_at: item.created_at, userId: item.userId, roomId: item.roomId, roomName: item.roomName, storyName: item.storyName, scale: item.scale, votes: item.votes, }; }) .concat({ id: "LATEST", created_at: new Date(), userId: roomFromDbParsed.userId, roomId: roomFromDbParsed.id, roomName: roomFromDbParsed.roomName, storyName: storyNameText, scale: roomScale, votes: votesFromDbParsed?.map((vote) => { return { value: vote.value, }; }), }); jsonToCsv(jsonObject!, `sp_${roomId}.csv`); } }; const copyRoomURLHandler = () => { navigator.clipboard .writeText(window.location.href) .then(() => { console.log(`Copied Room Link to Clipboard!`); setCopied(true); setTimeout(() => { setCopied(false); }, 2000); }) .catch(() => { console.log(`Error Copying Room Link to Clipboard!`); }); }; const voteString = ( visible: boolean, votes: typeof votesFromDbParsed, presenceItem: PresenceItem ) => { const matchedVote = votes?.find( (vote) => vote.userId === presenceItem.userId ); if (visible) { if (!!matchedVote) { return {matchedVote.value}; } else { return ; } } else if (!!matchedVote) { return ; } else { return ; } }; // Hooks // ================================= useEffect(() => { if (roomFromDb) { setStoryNameText(roomFromDbParsed?.storyName || ""); setRoomScale(roomFromDbParsed?.scale || "ERROR"); } }, [roomFromDb]); // UI // ================================= // Room is loading if (!roomFromDbParsed) { return ; // Room has been loaded } else { return ( {roomFromDbParsed?.roomName} ID: {roomFromDbParsed?.id} {copied ? ( ) : ( )} {roomFromDb && ( Story: {roomFromDbParsed?.storyName} {presenceData && presenceDateParsed ?.filter( (value, index, self) => index === self.findIndex( (presenceItem) => presenceItem.userId === value.userId ) ) .map((presenceItem) => { return ( {presenceItem.userFullName}{" "} {presenceItem.isAdmin && ( )}{" "} {presenceItem.isVIP && ( )}{" "} {presenceItem.userId === roomFromDbParsed?.userId && ( )} {" : "} {roomFromDb && votesFromDb && voteString( roomFromDbParsed?.visible!, votesFromDbParsed, presenceItem )} ); })} {roomFromDbParsed?.scale?.split(",").map((scaleItem, index) => { return ( void setVoteHandler(scaleItem)} > {scaleItem} ); })} )} {!!roomFromDbParsed && (roomFromDbParsed.userId === user?.id || isAdmin(user?.publicMetadata)) && ( <> Room Settings {"Vote Scale (Comma Separated):"}{" "} { setRoomScale(event.target.value); }} /> {"Story Name:"} { setStoryNameText(event.target.value); }} /> void setRoomHandler({ visible: !roomFromDbParsed?.visible, reset: false, log: false, }) } className="btn btn-primary inline-flex" > {roomFromDbParsed.visible ? ( <> Hide > ) : ( <> Show > )} void setRoomHandler({ visible: false, reset: true, log: roomFromDbParsed?.storyName === storyNameText || votesFromDb?.length === 0 ? false : true, }) } className="btn btn-primary inline-flex" disabled={ [...new Set(roomScale.split(","))].filter( (item) => item !== "" ).length <= 1 } > {roomFromDbParsed?.storyName === storyNameText || votesFromDb?.length === 0 ? ( <> Reset > ) : ( <> Save > )} {votesFromDb && (roomFromDbParsed?.logs.length > 0 || votesFromDb.length > 0) && ( downloadLogs()} className="btn btn-primary inline-flex hover:animate-pulse" > <> > )} > )} ); } }
{presenceItem.userFullName}{" "} {presenceItem.isAdmin && ( )}{" "} {presenceItem.isVIP && ( )}{" "} {presenceItem.userId === roomFromDbParsed?.userId && ( )} {" : "}