Skip to content

Commit

Permalink
Merge pull request #347 from SotSF/client-side-initializing-loading-s…
Browse files Browse the repository at this point in the history
…tate

Initial loading state / better navigation
  • Loading branch information
brollin authored Nov 30, 2024
2 parents 6122876 + a7bad82 commit 49257c9
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 85 deletions.
28 changes: 26 additions & 2 deletions src/components/ExperienceEditor/ExperienceEditorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,39 @@ import { LoadingOverlay } from "@/src/components/LoadingOverlay";

export const ExperienceEditorPage = observer(function ExperienceEditorPage() {
const store = useStore();
const { uiStore, experienceStore } = store;
const { uiStore, experienceStore, initializationState } = store;
const { loadingExperienceName } = experienceStore;

const router = useRouter();

// Initialize the store with the experience name from the URL
useEffect(() => {
if (store.initializedClientSide || !router.query.experienceName) return;
if (initializationState !== "uninitialized" || !router.isReady) return;
store.initializeClientSide(router.query.experienceName as string);
}, [
store,
initializationState,
experienceStore,
store.experienceName,
router.isReady,
router.query.experienceName,
]);

// Listen for url changes and load any new experience by name
useEffect(() => {
if (
initializationState !== "initialized" ||
!router.query.experienceName ||
store.experienceName === router.query.experienceName ||
loadingExperienceName === router.query.experienceName
)
return;
experienceStore.load(router.query.experienceName as string);
}, [
store,
experienceStore,
initializationState,
loadingExperienceName,
store.experienceName,
router.query.experienceName,
]);
Expand Down
8 changes: 4 additions & 4 deletions src/components/ExperienceThumbnail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { FaCamera } from "react-icons/fa";
export const ExperienceThumbnail = memo(function ExperienceThumbnail({
thumbnailURL,
onClick,
captureButton = false,
showCaptureButton = false,
}: {
thumbnailURL: string;
onClick?: () => void;
captureButton?: boolean;
showCaptureButton?: boolean;
}) {
if (!thumbnailURL && !captureButton) return null;
if (!thumbnailURL && !showCaptureButton) return null;
return (
<Box
position="relative"
Expand All @@ -24,7 +24,7 @@ export const ExperienceThumbnail = memo(function ExperienceThumbnail({
_hover={onClick ? { opacity: 0.8 } : undefined}
onClick={onClick}
>
{!thumbnailURL && captureButton && (
{!thumbnailURL && showCaptureButton && (
<HStack justify="center" width="100%" height="100%">
<FaCamera size={13} />
</HStack>
Expand Down
14 changes: 12 additions & 2 deletions src/components/KeyboardControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useSaveExperience } from "@/src/hooks/experience";
import { useStore } from "@/src/types/StoreContext";
import { action } from "mobx";
import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
import { useEffect } from "react";

type Props = {
Expand All @@ -14,6 +15,7 @@ export const KeyboardControls = observer(function KeyboardControls({
const store = useStore();
const { uiStore, experienceStore, audioStore } = store;

const router = useRouter();
const { saveExperience } = useSaveExperience();

useEffect(() => {
Expand All @@ -25,7 +27,7 @@ export const KeyboardControls = observer(function KeyboardControls({
saveExperience();
e.preventDefault();
} else if (editMode && e.key === "n" && (e.ctrlKey || e.metaKey)) {
experienceStore.loadEmptyExperience();
experienceStore.openEmptyExperience(router);
e.preventDefault();
}

Expand Down Expand Up @@ -95,7 +97,15 @@ export const KeyboardControls = observer(function KeyboardControls({
window.removeEventListener("copy", handleCopy);
window.removeEventListener("paste", handlePaste);
};
}, [store, uiStore, experienceStore, audioStore, editMode, saveExperience]);
}, [
store,
uiStore,
experienceStore,
audioStore,
editMode,
saveExperience,
router,
]);

return null;
});
8 changes: 6 additions & 2 deletions src/components/LoadingOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ const contextMatchesRole = (context: Context, role: Role) => {

export const LoadingOverlay = observer(function LoadingOverlay() {
const store = useStore();
const { context, role } = store;
const { context, role, experienceStore } = store;
return (
<Modal
isOpen={!contextMatchesRole(context, role)}
isOpen={
store.initializationState !== "initialized" ||
experienceStore.loadingExperienceName !== null ||
!contextMatchesRole(context, role)
}
onClose={() => {}}
closeOnEsc={false}
closeOnOverlayClick={false}
Expand Down
6 changes: 4 additions & 2 deletions src/components/LoginButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import { observer } from "mobx-react-lite";
import { trpc } from "@/src/utils/trpc";
import { sanitize } from "@/src/utils/sanitize";
import { CONJURER_USER } from "@/src/types/User";
import { useRouter } from "next/router";

export const LoginButton = observer(function LoginButton() {
const store = useStore();
const { experienceStore, uiStore, userStore, usingLocalData } = store;

const router = useRouter();
const [newUsername, setNewUsername] = useState("");

const {
Expand Down Expand Up @@ -80,7 +82,7 @@ export const LoginButton = observer(function LoginButton() {
width="100%"
onClick={action(() => {
userStore.me = user;
experienceStore.loadEmptyExperience();
experienceStore.openEmptyExperience(router);
if (store.context === "experienceEditor") {
uiStore.showingOpenExperienceModal = true;
}
Expand Down Expand Up @@ -110,7 +112,7 @@ export const LoginButton = observer(function LoginButton() {
username: newUsername,
});
userStore.me = newUser;
experienceStore.loadEmptyExperience();
experienceStore.openEmptyExperience(router);
onClose();
})}
>
Expand Down
22 changes: 12 additions & 10 deletions src/components/Menu/MenuBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ import { action } from "mobx";
import { LatencyModal } from "@/src/components/LatencyModal/LatencyModal";
import { ExperienceThumbnail } from "@/src/components/ExperienceThumbnail";
import { ExperienceStatusIndicator } from "../ExperienceStatusIndicator";
import { useRouter } from "next/router";

export const MenuBar = observer(function MenuBar() {
const store = useStore();
const { audioStore, experienceStore, uiStore } = store;

const router = useRouter();
const { saveExperience } = useSaveExperience();

const {
Expand Down Expand Up @@ -86,12 +88,11 @@ export const MenuBar = observer(function MenuBar() {
</Modal>
<LatencyModal />
<HStack>
{/* TODO: disallow editing if you don't own this experience */}
{store.context === "experienceEditor" ? (
{store.context === "experienceEditor" && store.canEditExperience ? (
<ExperienceThumbnail
thumbnailURL={store.experienceThumbnailURL}
onClick={action(() => (uiStore.capturingThumbnail = true))}
captureButton
showCaptureButton
/>
) : (
<ExperienceThumbnail thumbnailURL={store.experienceThumbnailURL} />
Expand Down Expand Up @@ -169,7 +170,9 @@ export const MenuBar = observer(function MenuBar() {
<MenuItem
icon={<FaFile size={17} />}
command="⌘N"
onClick={experienceStore.loadEmptyExperience}
onClick={() =>
experienceStore.openEmptyExperience(router)
}
>
New experience
</MenuItem>
Expand Down Expand Up @@ -373,17 +376,16 @@ export const MenuBar = observer(function MenuBar() {
py={0}
variant="ghost"
size="xs"
fontSize="xs"
transition="all 0.2s"
borderRadius="md"
_hover={{ bg: "gray.500" }}
_focus={{ boxShadow: "outline" }}
>
<Text fontSize="xs">
<ExperienceStatusIndicator
experienceStatus={store.experienceStatus}
withLabel
/>
</Text>
<ExperienceStatusIndicator
experienceStatus={store.experienceStatus}
withLabel
/>
</Button>
</Menu>
</HStack>
Expand Down
15 changes: 6 additions & 9 deletions src/components/Menu/OpenExperienceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,22 @@ import { action } from "mobx";
import { useState } from "react";
import { ExperiencesTable } from "@/src/components/ExperiencesTable/ExperiencesTable";
import { useExperiences } from "@/src/hooks/experiencesAndUsers";
import { useRouter } from "next/router";

export const OpenExperienceModal = observer(function OpenExperienceModal() {
const store = useStore();
const { experienceStore, uiStore, userStore } = store;
const { uiStore, userStore, experienceStore } = store;
const { username } = userStore;

const [viewingAllExperiences, setViewingAllExperiences] = useState(false);
const [isLoadingNewExperience, setIsLoadingNewExperience] = useState(false);

const { isPending, isError, isRefetching, experiences } = useExperiences({
username: viewingAllExperiences ? undefined : username,
enabled: uiStore.showingOpenExperienceModal,
});

const router = useRouter();

const onClose = action(() => (uiStore.showingOpenExperienceModal = false));

if (isError) return null;
Expand All @@ -45,10 +47,7 @@ export const OpenExperienceModal = observer(function OpenExperienceModal() {
<ModalOverlay />
<ModalContent>
<ModalHeader>
Open experience{" "}
{(isPending || isRefetching || isLoadingNewExperience) && (
<Spinner ml={2} />
)}
Open experience {(isPending || isRefetching) && <Spinner ml={2} />}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
Expand All @@ -63,9 +62,7 @@ export const OpenExperienceModal = observer(function OpenExperienceModal() {
<ExperiencesTable
experiences={experiences}
onClickExperience={action(async (experience) => {
setIsLoadingNewExperience(true);
await experienceStore.load(experience.name);
setIsLoadingNewExperience(false);
experienceStore.openExperience(router, experience.name);
onClose();
})}
/>
Expand Down
10 changes: 4 additions & 6 deletions src/components/PlaygroundPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef } from "react";
import { useEffect } from "react";
import { PatternPlayground } from "@/src/components/PatternPlayground/PatternPlayground";
import { useStore } from "@/src/types/StoreContext";
import { observer } from "mobx-react-lite";
Expand All @@ -10,15 +10,13 @@ import { LoginButton } from "@/src/components/LoginButton";
export const PlaygroundPage = observer(function PlaygroundPage() {
const store = useStore();

const initialized = useRef(false);
useEffect(() => {
if (initialized.current) return;
initialized.current = true;
if (store.initializationState !== "uninitialized") return;
store.initializeClientSide();
}, [store]);
}, [store, store.initializationState]);

return (
store.initializedClientSide && (
store.initializationState === "initialized" && (
<>
<HStack
p={2}
Expand Down
2 changes: 1 addition & 1 deletion src/components/PlaylistEditor/PlaylistEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const PlaylistEditor = observer(function PlaylistEditor() {
leftIcon={<FaPlus size={14} />}
onClick={() => {
store.role = "experienceCreator";
router.push("/experience/untitled");
experienceStore.openEmptyExperience(router);
}}
>
New experience
Expand Down
4 changes: 2 additions & 2 deletions src/components/PlaylistEditor/PlaylistEditorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export const PlaylistEditorPage = observer(function PlaylistEditorPage() {
const { userStore } = store;

useEffect(() => {
if (store.initializedClientSide) return;
if (store.initializationState !== "uninitialized") return;
store.initializeClientSide();
}, [store]);
}, [store, store.initializationState]);

return (
<Box position="relative" w="100vw" h="100vh">
Expand Down
2 changes: 1 addition & 1 deletion src/components/PlaylistEditor/PlaylistItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export const PlaylistItem = observer(function PlaylistItem({
icon={<FaPencilAlt size={10} />}
onClick={action(() => {
store.role = "experienceCreator";
router.push(`/experience/${experience.name}`);
experienceStore.openExperience(router, experience.name);
})}
/>
{editable && (
Expand Down
3 changes: 2 additions & 1 deletion src/components/RoleSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useRouter } from "next/router";

export const RoleSelector = observer(function RoleSelector() {
const store = useStore();
const { experienceStore } = store;
const router = useRouter();
return (
<Menu computePositionOnMount size="xs">
Expand Down Expand Up @@ -36,7 +37,7 @@ export const RoleSelector = observer(function RoleSelector() {
<MenuItem
onClick={action(() => {
store.role = "experienceCreator";
router.push(`/experience/${store.experienceName || "untitled"}`);
experienceStore.openExperience(router, store.experienceName);
})}
>
Experience Creator
Expand Down
Loading

0 comments on commit 49257c9

Please sign in to comment.