fix: restore working camera, fix PayPal donation link#10
Conversation
- Revert ImageCaptureModal to the original working version - Fix PayPal link: use paypal.com/donate with email parameter instead of broken paypal.me profile link https://claude.ai/code/session_018567acDi8JQ3BHH8BxYX5b
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 959c1f156e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| streamRef.current = stream; | ||
|
|
||
| if (videoRef.current) { | ||
| videoRef.current.srcObject = stream; | ||
| // Wait for video to actually start playing | ||
| await new Promise<void>((resolve, reject) => { | ||
| const video = videoRef.current!; | ||
| const timeout = setTimeout(() => reject(new Error('timeout')), 8000); | ||
| video.onloadedmetadata = () => { | ||
| clearTimeout(timeout); | ||
| video.play().then(resolve).catch(reject); | ||
| }; | ||
| video.onerror = () => { | ||
| clearTimeout(timeout); | ||
| reject(new Error('video error')); | ||
| }; | ||
| }); | ||
| if (mountedRef.current) { | ||
| setLoading(false); | ||
| } | ||
| } | ||
| } catch (err) { | ||
| if (!mountedRef.current) return; | ||
|
|
||
| const error = err as DOMException; | ||
| if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') { | ||
| setError('Kamera-Zugriff wurde verweigert. Bitte erlaube den Zugriff in den Browser-Einstellungen und versuche es erneut.'); | ||
| } else if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') { | ||
| setError('Keine Kamera gefunden. Stelle sicher, dass dein Gerät eine Kamera hat.'); | ||
| } else if (error.name === 'NotReadableError' || error.name === 'TrackStartError') { | ||
| setError('Kamera wird von einer anderen App verwendet. Schliesse andere Kamera-Apps und versuche es erneut.'); | ||
| } else if (error.name === 'OverconstrainedError') { | ||
| // Retry without specific facing mode | ||
| try { | ||
| const fallbackStream = await navigator.mediaDevices.getUserMedia({ | ||
| video: true, | ||
| audio: false, | ||
| }); | ||
| if (!mountedRef.current) { | ||
| fallbackStream.getTracks().forEach((t) => t.stop()); | ||
| return; | ||
| } | ||
| streamRef.current = fallbackStream; | ||
| if (videoRef.current) { | ||
| videoRef.current.srcObject = fallbackStream; | ||
| videoRef.current.onloadedmetadata = () => { | ||
| if (mountedRef.current) setLoading(false); | ||
| }; | ||
| } | ||
| return; | ||
| } catch { | ||
| setError('Kamera konnte nicht gestartet werden.'); | ||
| } | ||
| } else { | ||
| setError('Kamera konnte nicht gestartet werden. Bitte versuche es erneut.'); | ||
| videoRef.current.onloadedmetadata = () => setLoading(false); |
There was a problem hiding this comment.
Guard late getUserMedia result before storing stream
If the modal is closed while getUserMedia is still pending (for example, while the permission prompt is open), stopStream() runs before a stream exists, then this code still assigns the newly resolved stream to streamRef without checking whether the modal is still open. In that case the camera track can remain active in the background until the modal is reopened or the component unmounts, which is a privacy/battery regression introduced by removing the mounted/open guard.
Useful? React with 👍 / 👎.
https://claude.ai/code/session_018567acDi8JQ3BHH8BxYX5b