diff --git a/apps/web/src/pages/timelapse/[id].tsx b/apps/web/src/pages/timelapse/[id].tsx index 91dd96b..dbeb33b 100644 --- a/apps/web/src/pages/timelapse/[id].tsx +++ b/apps/web/src/pages/timelapse/[id].tsx @@ -43,17 +43,17 @@ export default function Page() { const [isPublishing, setIsPublishing] = useState(false); const [error, setError] = useState(null); const [errorIsCritical, setErrorIsCritical] = useState(false); - + const [editModalOpen, setEditModalOpen] = useState(false); const [editName, setEditName] = useState(""); const [editDescription, setEditDescription] = useState(""); const [editVisibility, setEditVisibility] = useState("PUBLIC"); const [isUpdating, setIsUpdating] = useState(false); - + const [passkeyModalOpen, setPasskeyModalOpen] = useState(false); const [missingDeviceName, setMissingDeviceName] = useState(""); const [invalidPasskeyAttempt, setInvalidPasskeyAttempt] = useState(false); - + const [syncModalOpen, setSyncModalOpen] = useState(false); const [hackatimeProject, setHackatimeProject] = useState(""); const [hackatimeProjects, setHackatimeProjects] = useState([]); @@ -66,6 +66,7 @@ export default function Page() { const [localComments, setLocalComments] = useState(timelapse?.comments ?? []); const [formattedDescription, setFormattedDescription] = useState(""); + useEffect(() => { if (!timelapse) return; @@ -75,7 +76,7 @@ export default function Page() { }, [timelapse]); const isOwned = timelapse && currentUser && currentUser.id === timelapse.owner.id; - + const videoRef = useRef(null); function setCriticalError(message: string) { @@ -145,9 +146,9 @@ export default function Page() { setCriticalError(`Failed to load timelapse video, HTTP ${vidRes.status}!`); return; } - + const vidData = await vidRes.arrayBuffer(); - + try { // Decrypt the video data using the device passkey and timelapse ID const decryptedData = await decryptVideo( @@ -155,7 +156,7 @@ export default function Page() { timelapse.id, originDevice.passkey ); - + // Create a blob from the decrypted data and assign it to the video element const videoBlob = new Blob([decryptedData], { type: "video/mp4" }); const url = URL.createObjectURL(videoBlob); @@ -178,7 +179,7 @@ export default function Page() { apiErr instanceof Error ? apiErr.message : "An unknown error occurred while loading the timelapse" - ); + ); } }, [router, router.isReady]); @@ -218,11 +219,11 @@ export default function Page() { if (result.ok) { setTimelapse(result.data.timelapse); - } + } else { setRegularError(`Failed to publish: ${result.error}`); } - } + } catch (error) { console.error("([id].tsx) error publishing timelapse:", error); setCriticalError( @@ -230,7 +231,7 @@ export default function Page() { ? error.message : "An error occurred while publishing the timelapse." ); - } + } finally { setIsPublishing(false); } @@ -264,15 +265,15 @@ export default function Page() { if (result.ok) { setTimelapse(result.data.timelapse); setEditModalOpen(false); - } + } else { setRegularError(`Failed to update: ${result.error}`); } - } + } catch (error) { console.error("([id].tsx) error updating timelapse:", error); setRegularError(error instanceof Error ? error.message : "An error occurred while updating the timelapse."); - } + } finally { setIsUpdating(false); } @@ -320,15 +321,15 @@ export default function Page() { setTimelapse(result.data.timelapse); setSyncModalOpen(false); setHackatimeProject(""); - } + } else { setRegularError(`Failed to sync with Hackatime: ${result.error}`); } - } + } catch (error) { console.error("([id].tsx) error syncing with Hackatime:", error); setRegularError(error instanceof Error ? error.message : "An error occurred while syncing with Hackatime."); - } + } finally { setIsSyncing(false); } @@ -392,12 +393,42 @@ export default function Page() { } } + async function handleReport() { + if (!timelapse) return; + + const reason = prompt("Why are you reporting this timelapse?"); + if (!reason) + return; + + try { + const result = await trpc.report.create.mutate({ + timelapseId: timelapse.id, + reason, + }); + + if (result.ok) { + alert("Report submitted. Thank you!"); + } + else { + setRegularError(result.message); + } + } + catch (error) { + console.error("([id].tsx) error reporting timelapse:", error); + setRegularError( + error instanceof Error + ? error.message + : "An error occurred while submitting the report." + ); + } + } + return (
-
@@ -442,20 +475,20 @@ export default function Page() {

- { timelapse?.name || } - - { timelapse && !timelapse.isPublished && ( + {timelapse?.name || } + + {timelapse && !timelapse.isPublished && ( UNPUBLISHED )} - { timelapse && timelapse.isPublished && timelapse.visibility === "UNLISTED" && ( + {timelapse && timelapse.isPublished && timelapse.visibility === "UNLISTED" && ( UNLISTED - ) } + )}

- +
- - by { !timelapse ? : @{timelapse.owner.displayName} } + by {!timelapse ? : @{timelapse.owner.displayName}} - { !timelapse ? : <> } + {!timelapse ? : <> }

- { timelapse != null ? formattedDescription : } + {timelapse != null ? formattedDescription : }

- - { timelapse && timelapse.isPublished && timelapse.visibility === "UNLISTED" && isOwned && ( + + {timelapse && timelapse.isPublished && timelapse.visibility === "UNLISTED" && isOwned && (

@@ -489,7 +522,7 @@ export default function Page() {

)} - { timelapse && timelapse.isPublished && } + {timelapse && timelapse.isPublished && } @@ -528,7 +561,7 @@ export default function Page() { {isUpdating ? "Updating..." : "Update"} - { !timelapse?.isPublished && ( + {!timelapse?.isPublished && (