From 70a066abf3a0cfcf22660ab6cc03294a81a2e14e Mon Sep 17 00:00:00 2001 From: Ben Rollin Date: Wed, 30 Oct 2024 01:10:40 -0700 Subject: [PATCH 01/46] user->username --- src/components/Display.tsx | 2 +- src/components/LoadBeatMapModal.tsx | 2 +- src/components/LoginModal.tsx | 10 +++++----- src/components/Menu/OpenExperienceModal.tsx | 8 +++++--- src/components/Menu/SaveExperienceModal.tsx | 6 +++--- src/hooks/experience.ts | 6 +++--- src/types/ExperienceStore.ts | 1 - src/types/Store.ts | 14 +++++++------- src/types/UIStore.ts | 6 +++--- 9 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/components/Display.tsx b/src/components/Display.tsx index 1559f09d..2f036b7f 100644 --- a/src/components/Display.tsx +++ b/src/components/Display.tsx @@ -38,7 +38,7 @@ export const Display = observer(function Display() { {store.context === "viewer" ? ( - by {store.user} + by {store.username} ) : ( )} diff --git a/src/components/LoadBeatMapModal.tsx b/src/components/LoadBeatMapModal.tsx index b826d3ca..b5a2c894 100644 --- a/src/components/LoadBeatMapModal.tsx +++ b/src/components/LoadBeatMapModal.tsx @@ -58,7 +58,7 @@ export const LoadBeatMapModal = observer(function LoadBeatMapModal() { <> {beatMaps.length === 0 && ( - {store.user} has no saved experiences yet! + {store.username} has no saved experiences yet! )} diff --git a/src/components/LoginModal.tsx b/src/components/LoginModal.tsx index daecada0..9d773f68 100644 --- a/src/components/LoginModal.tsx +++ b/src/components/LoginModal.tsx @@ -22,7 +22,7 @@ import { trpc } from "@/src/utils/trpc"; export const LoginModal = observer(function LoginModal() { const store = useStore(); - const { experienceStore, uiStore, user, usingLocalData } = store; + const { experienceStore, uiStore, usingLocalData } = store; const [newUser, setNewUser] = useState(""); @@ -52,7 +52,7 @@ export const LoginModal = observer(function LoginModal() { leftIcon={} size="xs" > - {user || "Log in"} + {store.username || "Log in"} {!isPending && users - .filter(({ username }) => username !== user) + .filter((user) => user.username !== store.username) .map((user) => ( diff --git a/src/pages/experience/[experienceName].tsx b/src/pages/experience/[experienceName].tsx new file mode 100644 index 00000000..ee9647c4 --- /dev/null +++ b/src/pages/experience/[experienceName].tsx @@ -0,0 +1,26 @@ +import { App } from "@/src/components/App"; +import { Store } from "@/src/types/Store"; +import { StoreContext } from "@/src/types/StoreContext"; +import { ChakraProvider, theme } from "@chakra-ui/react"; +import Head from "next/head"; +import { useMemo } from "react"; + +export default function Home() { + const store = useMemo(() => new Store("default"), []); + return ( + <> + + Conjurer + + + + + + + + + + + + ); +} diff --git a/src/types/Store.ts b/src/types/Store.ts index ab727190..a0b73cf8 100644 --- a/src/types/Store.ts +++ b/src/types/Store.ts @@ -114,14 +114,16 @@ export class Store { if (this.context === "default") localStorage.setItem("username", value); } - private _experienceName = "untitled"; + private _experienceName = ""; get experienceName(): string { return this._experienceName; } set experienceName(value: string) { this._experienceName = value; - if (this.context === "default") + if (this.context === "default") { localStorage.setItem("experienceName", value); + window.history.pushState({}, "", `/experience/${value}`); + } } hasSaved = false; @@ -154,7 +156,7 @@ export class Store { } }; - initializeClientSide = () => { + initializeClientSide = (initialExperienceName?: string) => { if (this.initializedClientSide) return; this.initializedClientSide = true; @@ -197,9 +199,8 @@ export class Store { if (usingLocalData && process.env.NEXT_PUBLIC_NODE_ENV !== "production") this._usingLocalData = usingLocalData === "true"; - // check for an experience name in local storage - const experienceName = localStorage.getItem("experienceName"); - if (experienceName) this.experienceStore.load(experienceName); + // load experience from path parameter if provided (e.g. /experience/my-experience) + if (initialExperienceName) this.experienceStore.load(initialExperienceName); else this.experienceStore.loadEmptyExperience(); this.uiStore.initialize(); From 68275654255d2a7a6c5e159b859f17be8dda18b5 Mon Sep 17 00:00:00 2001 From: Ben Rollin Date: Thu, 31 Oct 2024 04:49:53 -0700 Subject: [PATCH 03/46] better open experience modal --- src/components/Menu/OpenExperienceModal.tsx | 111 +++++++++++++----- src/components/Menu/SaveExperienceModal.tsx | 2 +- .../PlaylistEditor/AddExperienceModal.tsx | 6 +- src/hooks/experience.ts | 3 +- src/server/routers/experienceRouter.ts | 56 +++++++-- 5 files changed, 134 insertions(+), 44 deletions(-) diff --git a/src/components/Menu/OpenExperienceModal.tsx b/src/components/Menu/OpenExperienceModal.tsx index 37e967f3..858cf22b 100644 --- a/src/components/Menu/OpenExperienceModal.tsx +++ b/src/components/Menu/OpenExperienceModal.tsx @@ -1,6 +1,7 @@ import { observer } from "mobx-react-lite"; import { Button, + IconButton, Modal, ModalBody, ModalCloseButton, @@ -9,37 +10,51 @@ import { ModalHeader, ModalOverlay, Spinner, + Switch, + Table, + TableContainer, + Tbody, + Td, Text, - VStack, + Th, + Thead, + Tr, } from "@chakra-ui/react"; import { useStore } from "@/src/types/StoreContext"; import { action } from "mobx"; import { trpc } from "@/src/utils/trpc"; -import { GiSparkles } from "react-icons/gi"; import { useState } from "react"; +import { FaTrashAlt } from "react-icons/fa"; export const OpenExperienceModal = observer(function OpenExperienceModal() { const store = useStore(); const { experienceStore, uiStore, username, usingLocalData } = store; + const [filterMyExperiencesOnly, setFilterMyExperiencesOnly] = useState(true); + const [isLoadingNewExperience, setIsLoadingNewExperience] = useState(false); + const { isPending, isError, - data: experiences, + data: experiencesAndUsers, isRefetching, - } = trpc.experience.listExperiences.useQuery( - { username, usingLocalData }, + } = trpc.experience.listExperiencesAndUsers.useQuery( + { + username: filterMyExperiencesOnly ? username : undefined, + usingLocalData, + }, { enabled: uiStore.showingOpenExperienceModal } ); - const [isLoadingNewExperience, setIsLoadingNewExperience] = useState(false); - const onClose = action(() => (uiStore.showingOpenExperienceModal = false)); if (isError) return null; - const sortedExperiences = (experiences ?? []).sort((a, b) => - a.name.localeCompare(b.name) + // TODO: extract into a utility function + const sortedExperiencesAndUsers = (experiencesAndUsers ?? []).sort((a, b) => + `${a.user.username}${a.experience.name}`.localeCompare( + `${b.user.username}${b.experience.name}` + ) ); return ( @@ -47,6 +62,7 @@ export const OpenExperienceModal = observer(function OpenExperienceModal() { onClose={onClose} isOpen={uiStore.showingOpenExperienceModal} isCentered + size="4xl" > @@ -58,30 +74,69 @@ export const OpenExperienceModal = observer(function OpenExperienceModal() { - {!isPending && experiences.length === 0 && ( + {!isPending && experiencesAndUsers.length === 0 && ( {username} has no saved experiences yet! )} + setFilterMyExperiencesOnly(e.target.checked)} + > + Only my experiences + {!isPending && ( - - {sortedExperiences.map((experience) => ( - - ))} - + + + + + + + + + + + {sortedExperiencesAndUsers.map(({ user, experience }) => ( + + + + + + + ))} + +
AuthorNameSong +
{user.username} + + + {experience.song?.artist} - {experience.song?.name} + + {user.username === username && ( + } + disabled={true} + /> + )} +
+
)}
diff --git a/src/components/Menu/SaveExperienceModal.tsx b/src/components/Menu/SaveExperienceModal.tsx index f29936ba..f0c257f3 100644 --- a/src/components/Menu/SaveExperienceModal.tsx +++ b/src/components/Menu/SaveExperienceModal.tsx @@ -27,7 +27,7 @@ export const SaveExperienceModal = observer(function SaveExperienceModal() { isPending, isError, data: experiences, - } = trpc.experience.listExperiences.useQuery( + } = trpc.experience.listExperiencesForUser.useQuery( { username, usingLocalData }, { enabled: uiStore.showingSaveExperienceModal } ); diff --git a/src/components/PlaylistEditor/AddExperienceModal.tsx b/src/components/PlaylistEditor/AddExperienceModal.tsx index 2314d98a..df739f88 100644 --- a/src/components/PlaylistEditor/AddExperienceModal.tsx +++ b/src/components/PlaylistEditor/AddExperienceModal.tsx @@ -21,8 +21,8 @@ export const AddExperienceModal = observer(function AddExperienceModal() { const { isPending, isError, - data: experiences, - } = trpc.experience.listExperiences.useQuery( + data: experiencesAndUsers, + } = trpc.experience.listExperiencesAndUsers.useQuery( { usingLocalData }, { enabled: uiStore.showingPlaylistAddExperienceModal } ); @@ -47,7 +47,7 @@ export const AddExperienceModal = observer(function AddExperienceModal() { {!isPending && - experiences.map((experience) => ( + experiencesAndUsers.map(({ experience }) => (