Skip to content

Commit 6189e09

Browse files
authored
Replace Jotai with Zustand dialogs store and add robots.txt (#9)
2 parents 1aabb61 + 837e451 commit 6189e09

File tree

11 files changed

+230
-145
lines changed

11 files changed

+230
-145
lines changed

app/components/dialogs/delete-video-dialog.tsx

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,26 @@ import {
66
CredenzaHeader,
77
CredenzaTitle,
88
} from "@/components/ui/credenza";
9-
import { useAtom } from "jotai";
109
import { toast } from "sonner";
11-
import { deleteVideoAtom } from "@/lib/atoms";
1210
import { usageDataQueryOptions, videosQueryOptions } from "@/lib/query-utils";
1311
import { notNanOrDefault } from "@/lib/utils";
1412
import { queryClient } from "@/routes/__root";
1513
import { deleteVideoServerFn } from "@/server-fns/videos";
14+
import { useDialogsStore } from "@/lib/stores/dialogs";
1615

1716
export function DeleteVideoDialog() {
18-
const [deleteVideoData, setDeleteVideoData] = useAtom(deleteVideoAtom);
17+
const closeDeleteVideoDialog = useDialogsStore(
18+
(state) => state.closeDeleteVideoDialog
19+
);
20+
const deleteVideoDialogData = useDialogsStore(
21+
(state) => state.deleteVideoDialogData
22+
);
23+
const isDeleteVideoDialogOpen = useDialogsStore(
24+
(state) => state.isDeleteVideoDialogOpen
25+
);
1926

2027
async function doDeleteVideo() {
21-
if (!deleteVideoData) {
28+
if (!isDeleteVideoDialogOpen || !deleteVideoDialogData) {
2229
return;
2330
}
2431

@@ -28,7 +35,6 @@ export function DeleteVideoDialog() {
2835
);
2936

3037
function reset(message?: string) {
31-
console.log("resetting videos to ", oldVideos);
3238
queryClient.setQueryData(usageDataQueryOptions.queryKey, oldUsageData);
3339

3440
queryClient.setQueryData(videosQueryOptions.queryKey, (old) => {
@@ -45,7 +51,7 @@ export function DeleteVideoDialog() {
4551
const videos =
4652
queryClient.getQueryData(videosQueryOptions.queryKey)?.videos ?? [];
4753

48-
const video = videos.find((v) => v.id === deleteVideoData.id);
54+
const video = videos.find((v) => v.id === deleteVideoDialogData.videoId);
4955

5056
queryClient.setQueryData(usageDataQueryOptions.queryKey, (old) => {
5157
console.log(old);
@@ -62,15 +68,15 @@ export function DeleteVideoDialog() {
6268
queryClient.setQueryData(videosQueryOptions.queryKey, (old) => {
6369
return {
6470
...old,
65-
videos: videos.filter((v) => v.id !== deleteVideoData.id),
71+
videos: videos.filter((v) => v.id !== deleteVideoDialogData.videoId),
6672
};
6773
});
6874

69-
setDeleteVideoData(undefined);
75+
closeDeleteVideoDialog();
7076

7177
toast.promise(
7278
deleteVideoServerFn({
73-
data: { videoId: deleteVideoData.id },
79+
data: { videoId: deleteVideoDialogData.videoId },
7480
}),
7581
{
7682
loading: "Queueing video for deletion...",
@@ -84,34 +90,29 @@ export function DeleteVideoDialog() {
8490
}
8591
}
8692

87-
if (deleteVideoData === undefined) {
93+
if (!isDeleteVideoDialogOpen || !deleteVideoDialogData) {
8894
return null;
8995
}
9096

9197
return (
9298
<Credenza
9399
onOpenChange={(o) => {
94100
if (!o) {
95-
setDeleteVideoData(undefined);
101+
closeDeleteVideoDialog();
96102
}
97103
}}
98-
open={deleteVideoData !== undefined}
104+
open={isDeleteVideoDialogOpen}
99105
>
100106
<CredenzaContent>
101107
<CredenzaHeader>
102108
<CredenzaTitle>Confirm Video Deletion</CredenzaTitle>
103109
<CredenzaDescription>
104110
Are you sure you want to delete{" "}
105-
<strong>{deleteVideoData.name}</strong>?
111+
<strong>{deleteVideoDialogData.videoTitle}</strong>?
106112
</CredenzaDescription>
107113
</CredenzaHeader>
108114
<div className="flex gap-2 items-end">
109-
<Button
110-
className="grow"
111-
onMouseDown={() => {
112-
setDeleteVideoData(undefined);
113-
}}
114-
>
115+
<Button className="grow" onMouseDown={closeDeleteVideoDialog}>
115116
Cancel
116117
</Button>
117118
<Button

app/components/dialogs/edit-video-dialog.tsx

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@ import { Input } from "@/components/ui/input";
99
import { Label } from "@/components/ui/label";
1010
import { Switch } from "@/components/ui/switch";
1111
import { useForm } from "@tanstack/react-form";
12-
import { useAtom } from "jotai";
1312
import { useEffect } from "react";
1413
import { toast } from "sonner";
1514
import { FieldInfo } from "./upload-video-dialog";
1615
import { useRouter } from "@tanstack/react-router";
17-
import { editVideoAtom } from "@/lib/atoms";
1816
import { updateVideoDataServerFn } from "@/server-fns/videos";
1917
import { useQuery, useQueryClient } from "@tanstack/react-query";
2018
import { videosQueryOptions } from "@/lib/query-utils";
19+
import { useDialogsStore } from "@/lib/stores/dialogs";
2120

2221
type FormData = {
2322
title?: string;
@@ -28,31 +27,42 @@ export function EditVideoDialog() {
2827
const router = useRouter();
2928
const queryClient = useQueryClient();
3029
const { data } = useQuery(videosQueryOptions);
31-
const [editVideo, setEditVideo] = useAtom(editVideoAtom);
3230

33-
const videoData = data?.videos.find((v) => v.id === editVideo?.id);
31+
const editVideoDialogData = useDialogsStore(
32+
(state) => state.editVideoDialogData
33+
);
34+
const isEditVideoDialogOpen = useDialogsStore(
35+
(state) => state.isEditVideoDialogOpen
36+
);
37+
const closeEditVideoDialog = useDialogsStore(
38+
(state) => state.closeEditVideoDialog
39+
);
40+
41+
const videoData = data?.videos.find(
42+
(v) => v.id === editVideoDialogData?.videoId
43+
);
3444

3545
async function handleUpdateVideo(data: FormData) {
3646
try {
37-
if (editVideo === undefined) {
47+
if (editVideoDialogData === undefined) {
3848
return;
3949
}
4050

4151
// Optimistically update the UI
4252
queryClient.setQueryData(videosQueryOptions.queryKey, (old) => ({
4353
...old,
4454
videos: (old?.videos ?? []).map((v) => {
45-
if (v.id === editVideo.id) {
55+
if (v.id === editVideoDialogData.videoId) {
4656
return { ...v, ...data };
4757
}
4858
return v;
4959
}),
5060
}));
5161

52-
setEditVideo(undefined);
62+
closeEditVideoDialog();
5363

5464
const result = await updateVideoDataServerFn({
55-
data: { videoId: editVideo.id, data },
65+
data: { videoId: editVideoDialogData.videoId, data },
5666
});
5767

5868
if (!result.success) {
@@ -84,38 +94,38 @@ export function EditVideoDialog() {
8494
if (!form.state.isPristine) {
8595
handleUpdateVideo(value);
8696
} else {
87-
setEditVideo(undefined);
97+
closeEditVideoDialog();
8898
}
8999
form.reset();
90100
},
91101
});
92102

93-
// biome-ignore lint/correctness/useExhaustiveDependencies: only should react to editVideo changes
103+
// biome-ignore lint/correctness/useExhaustiveDependencies: only should react to dialog open state changes
94104
useEffect(() => {
95105
form.reset();
96-
}, [editVideo]);
106+
}, [isEditVideoDialogOpen]);
97107

98-
if (editVideo === undefined) {
108+
if (!isEditVideoDialogOpen || !editVideoDialogData) {
99109
return null;
100110
}
101111

102112
if (videoData === undefined) {
103-
setEditVideo(undefined);
113+
closeEditVideoDialog();
104114
return null;
105115
}
106116

107117
return (
108118
<Dialog
109119
onOpenChange={(o) => {
110120
if (!o) {
111-
setEditVideo(undefined);
121+
closeEditVideoDialog();
112122
}
113123
}}
114-
open={editVideo !== undefined}
124+
open={isEditVideoDialogOpen}
115125
>
116126
<DialogContent>
117127
<DialogHeader>
118-
<DialogTitle>Edit {editVideo.name}</DialogTitle>
128+
<DialogTitle>Edit {editVideoDialogData.videoTitle}</DialogTitle>
119129
</DialogHeader>
120130
<form
121131
className="flex flex-col gap-2"
@@ -172,12 +182,7 @@ export function EditVideoDialog() {
172182
}}
173183
/>
174184
<div className="flex gap-2 mt-2">
175-
<Button
176-
className="grow basis-1/2"
177-
onClick={() => {
178-
setEditVideo(undefined);
179-
}}
180-
>
185+
<Button className="grow basis-1/2" onClick={closeEditVideoDialog}>
181186
Cancel
182187
</Button>
183188
<Button

app/components/dialogs/trim-video-dialog.tsx

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,42 @@ import { RangeSlider } from "@/components/ui/range-slider";
99
import { formatSecondsToTimestamp } from "@/lib/utils";
1010
import { FFmpeg } from "@ffmpeg/ffmpeg";
1111
import { fetchFile, toBlobURL } from "@ffmpeg/util";
12-
import {
13-
QueryClient,
14-
QueryClientProvider,
15-
useMutation,
16-
useQuery,
17-
} from "@tanstack/react-query";
18-
import { useAtom, useSetAtom } from "jotai";
12+
import { useMutation, useQuery } from "@tanstack/react-query";
1913
import { Loader2Icon, PauseIcon, PlayIcon } from "lucide-react";
2014
import { useEffect, useMemo, useRef, useState } from "react";
21-
import {
22-
customFileToUploadAtom,
23-
isUploadDialogOpenAtom,
24-
trimVideoDataAtom,
25-
} from "@/lib/atoms";
15+
import { useDialogsStore } from "@/lib/stores/dialogs";
2616

2717
export function TrimVideoDialog() {
28-
const [trimVideoData] = useAtom(trimVideoDataAtom);
29-
const [queryClient] = useState(new QueryClient());
18+
const isTrimVideoDialogOpen = useDialogsStore(
19+
(state) => state.isTrimVideoDialogOpen
20+
);
21+
const trimVideoDialogData = useDialogsStore(
22+
(state) => state.trimVideoDialogData
23+
);
3024

31-
if (!trimVideoData) {
25+
if (!isTrimVideoDialogOpen || !trimVideoDialogData) {
3226
return null;
3327
}
3428

35-
return (
36-
<QueryClientProvider client={queryClient}>
37-
<TrimVideoDialogChild />
38-
</QueryClientProvider>
39-
);
29+
return <TrimVideoDialogChild />;
4030
}
4131

4232
function TrimVideoDialogChild() {
43-
const [trimVideoData, setTrimVideoData] = useAtom(trimVideoDataAtom);
44-
const setCustomFileToUploadAtom = useSetAtom(customFileToUploadAtom);
45-
const setIsUploadDialogOpenAtom = useSetAtom(isUploadDialogOpenAtom);
33+
const closeTrimVideoDialog = useDialogsStore(
34+
(state) => state.closeTrimVideoDialog
35+
);
36+
const trimVideoDialogData = useDialogsStore(
37+
(state) => state.trimVideoDialogData
38+
);
39+
const openTrimVideoDialog = useDialogsStore(
40+
(state) => state.openTrimVideoDialog
41+
);
42+
const openUploadVideoDialog = useDialogsStore(
43+
(state) => state.openUploadVideoDialog
44+
);
45+
const isTrimVideoDialogOpen = useDialogsStore(
46+
(state) => state.isTrimVideoDialogOpen
47+
);
4648

4749
const ffmpegRef = useRef(new FFmpeg());
4850
const viewportRef = useRef<HTMLVideoElement>(null);
@@ -52,19 +54,23 @@ function TrimVideoDialogChild() {
5254
const [videoDurationSeconds, setVideoDurationSeconds] = useState<number>();
5355

5456
const videoUrl = useMemo(
55-
() => URL.createObjectURL(trimVideoData!.file),
56-
[trimVideoData]
57+
() =>
58+
trimVideoDialogData
59+
? URL.createObjectURL(trimVideoDialogData.videoFile)
60+
: "",
61+
[trimVideoDialogData]
5762
);
5863

5964
const { isLoading } = useQuery({
60-
queryKey: ["loadFfmpeg"],
6165
refetchOnMount: true,
6266
refetchOnReconnect: false,
6367
refetchOnWindowFocus: false,
68+
staleTime: 0,
69+
queryKey: ["loadFfmpeg"],
6470
queryFn: async () => {
6571
const ffmpeg = ffmpegRef.current;
6672

67-
const baseURL = "https://unpkg.com/@ffmpeg/[email protected]/dist/esm";
73+
const baseURL = "https://unpkg.com/@ffmpeg/[email protected]/dist/umd";
6874

6975
await ffmpeg.load({
7076
coreURL: await toBlobURL(
@@ -87,15 +93,18 @@ function TrimVideoDialogChild() {
8793
isError: isRenderError,
8894
} = useMutation({
8995
mutationFn: async () => {
90-
if (!lastRangeValue.current || !trimVideoData) {
96+
if (!lastRangeValue.current || !trimVideoDialogData) {
9197
return false;
9298
}
9399

94100
resetViewportToBeginning();
95101

96102
const ffmpeg = ffmpegRef.current;
97103

98-
await ffmpeg.writeFile("input.mp4", await fetchFile(trimVideoData.file));
104+
await ffmpeg.writeFile(
105+
"input.mp4",
106+
await fetchFile(trimVideoDialogData.videoFile)
107+
);
99108

100109
const startTimestamp = formatSecondsToTimestamp(
101110
lastRangeValue.current[0]
@@ -116,13 +125,15 @@ function TrimVideoDialogChild() {
116125

117126
const data = await ffmpeg.readFile("output_video.mp4");
118127

119-
const file = new File([new Blob([data])], trimVideoData.file.name, {
120-
type: trimVideoData?.file.type,
121-
});
122-
123-
console.log(file.type);
128+
const file = new File(
129+
[new Blob([data])],
130+
trimVideoDialogData.videoFile.name,
131+
{
132+
type: trimVideoDialogData.videoFile.type,
133+
}
134+
);
124135

125-
setTrimVideoData({ title: trimVideoData?.title ?? null, file });
136+
openTrimVideoDialog(file, trimVideoDialogData.videoTitle);
126137
},
127138
onError: (err) => {
128139
console.error(err);
@@ -178,9 +189,11 @@ function TrimVideoDialogChild() {
178189
}, [isViewportPlaying]);
179190

180191
function handleBackToUploadClick() {
181-
setTrimVideoData(undefined);
182-
setCustomFileToUploadAtom(trimVideoData?.file);
183-
setIsUploadDialogOpenAtom(true);
192+
openUploadVideoDialog(
193+
trimVideoDialogData?.videoFile,
194+
trimVideoDialogData?.videoTitle
195+
);
196+
closeTrimVideoDialog();
184197
}
185198

186199
function handleRangeValueChange(value: [number, number]) {
@@ -236,10 +249,10 @@ function TrimVideoDialogChild() {
236249
viewportRef.current.src = "";
237250
}
238251

239-
setTrimVideoData(undefined);
252+
closeTrimVideoDialog();
240253
}
241254
}}
242-
open={trimVideoData !== undefined}
255+
open={isTrimVideoDialogOpen}
243256
>
244257
<DialogContent>
245258
<DialogHeader>

0 commit comments

Comments
 (0)