Files
DentalManagement09/apps/Frontend/src/hooks/use-screen-capture.ts
T
GiteadandClaude Sonnet 5 e49dfdbee9 feat: add attachment upload to Edit Appointment form (disk, scanned, screenshot with crop)
Lets staff attach files to an appointment directly from the edit form via
local disk upload, picking a previously-saved scanned attachment, or an
inline screenshot with crop-before-save (matching the Copy Agent page's
crop UX), instead of detouring through the Copy Agent page. Also fixes a
latent data-loss bug in claim-form's save-for-appointment call, which
replaced all AppointmentFile rows instead of merging with existing ones.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 14:55:38 -04:00

70 lines
2.4 KiB
TypeScript

import { useRef, useState } from "react";
// Minimal single-shot screen capture, extracted from the batching/cropping logic in
// ai-copy-agent-page.tsx — this just needs "take a screenshot, attach it."
export function useScreenCapture() {
const [isCapturing, setIsCapturing] = useState(false);
const [countdown, setCountdown] = useState<number | null>(null);
const streamRef = useRef<MediaStream | null>(null);
const stopStream = () => {
streamRef.current?.getTracks().forEach((track) => track.stop());
streamRef.current = null;
};
// Resolves with a PNG data URL of the full screenshot (caller crops before converting to a File).
async function capture(): Promise<string> {
setIsCapturing(true);
try {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: { displaySurface: "monitor" },
});
streamRef.current = stream;
const video = document.createElement("video");
video.srcObject = stream;
video.muted = true;
await video.play();
await new Promise<void>((resolve) => {
if (video.readyState >= 2) resolve();
else video.onloadeddata = () => resolve();
});
// Short countdown so the user has time to alt-tab to the window they want to capture.
for (let secondsLeft = 2; secondsLeft > 0; secondsLeft--) {
setCountdown(secondsLeft);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
setCountdown(null);
const canvas = document.createElement("canvas");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext("2d")?.drawImage(video, 0, 0, canvas.width, canvas.height);
stopStream();
return canvas.toDataURL("image/png");
} catch (error) {
stopStream();
const name = error instanceof Error ? error.name : "";
const message =
name === "NotAllowedError"
? "Screen share permission was denied or the picker was cancelled."
: name === "NotFoundError"
? "No screen/window source was available to capture."
: !navigator.mediaDevices?.getDisplayMedia
? "Screen capture isn't supported in this browser."
: error instanceof Error
? error.message
: "Screen capture failed.";
throw new Error(message);
} finally {
setIsCapturing(false);
setCountdown(null);
}
}
return { capture, isCapturing, countdown };
}