diff --git a/src/components/player/ProgressSlider.js b/src/components/player/ProgressSlider.js index 2f67b46..683bb84 100644 --- a/src/components/player/ProgressSlider.js +++ b/src/components/player/ProgressSlider.js @@ -46,7 +46,7 @@ const ProgressSlider = ({ if (isPlaying && typeof onPause === "function") { onPause(); } - }, [disabled, safeDuration, isPlaying, onPause, onSeekStart]); + }, [disabled, safeDuration, isPlaying, onPause, onSeekStart, progress]); const handleSeek = useCallback( (ratio) => { @@ -56,7 +56,7 @@ const ProgressSlider = ({ const targetMs = safeDuration * bounded; onSeek(targetMs); }, - [disabled, onSeek, safeDuration], + [disabled, onSeek, safeDuration] ); const handleSeekEnd = useCallback(() => { @@ -77,7 +77,7 @@ const ProgressSlider = ({ value={formatTime( isSeeking && previewRatio != null ? safeDuration * previewRatio - : safePosition, + : safePosition )} maxValue={formatTime(safeDuration)} progress={progress} diff --git a/src/hooks/useSearch.js b/src/hooks/useSearch.js index d7063f7..9eb1d80 100644 --- a/src/hooks/useSearch.js +++ b/src/hooks/useSearch.js @@ -31,7 +31,7 @@ const hasCoverAsset = (project) => { ...cover.options.flatMap((option) => [ option?.finalUrl, option?.generatedUrl, - ]), + ]) ); } return coverCandidates.some(isValidUri); @@ -43,7 +43,7 @@ const hasThumbnailAsset = (project) => const mergeProjectAssets = ( project, firestoreData, - { mergeCover = false, mergeThumbnails = false } = {}, + { mergeCover = false, mergeThumbnails = false } = {} ) => { if (!firestoreData || typeof firestoreData !== "object") { return project; @@ -51,7 +51,10 @@ const mergeProjectAssets = ( const mergedProject = { ...project }; if (mergeCover) { - if (!isValidUri(mergedProject.coverUrl) && isValidUri(firestoreData.coverUrl)) { + if ( + !isValidUri(mergedProject.coverUrl) && + isValidUri(firestoreData.coverUrl) + ) { mergedProject.coverUrl = firestoreData.coverUrl; } const firestoreCover = @@ -67,7 +70,10 @@ const mergeProjectAssets = ( } if (mergeThumbnails) { - if (!isValidUri(mergedProject.thumbnailUrl) && isValidUri(firestoreData.thumbnailUrl)) { + if ( + !isValidUri(mergedProject.thumbnailUrl) && + isValidUri(firestoreData.thumbnailUrl) + ) { mergedProject.thumbnailUrl = firestoreData.thumbnailUrl; } if ( @@ -83,7 +89,7 @@ const mergeProjectAssets = ( const hydrateProjects = async ( projects = [], - { ensureCover = false, ensureThumbnails = false } = {}, + { ensureCover = false, ensureThumbnails = false } = {} ) => { if (!Array.isArray(projects) || projects.length === 0) { return []; @@ -112,7 +118,7 @@ const hydrateProjects = async ( console.log("useSearch.hydrateProjects error", error?.message || error); return project; } - }), + }) ); }; @@ -132,6 +138,7 @@ const useSearch = () => { filters: `NOT objectID:${currentUID}`, }, }); + console.log("users are ", JSON.stringify(users, null, 2)); const { hits: playbacks, loading: playbackLoading } = useAlgoliaSearch({ query: search, algoliaObject: AlgoliaProjectConfig, diff --git a/src/screens/LandingPage.js b/src/screens/LandingPage.js index 74ef25b..812ed28 100644 --- a/src/screens/LandingPage.js +++ b/src/screens/LandingPage.js @@ -20,6 +20,7 @@ import { Routes } from "../navigation/Routes"; import { useUser } from "../providers/UserDataProvider"; import { Palette } from "../styles"; import { FONT_FAMILY } from "../styles/Fonts"; +import { BlurView } from "expo-blur"; const LANGUAGE_STORAGE_KEY = "preferredLanguage"; @@ -120,12 +121,7 @@ export default function LandingPage() { {selectedLanguage && ( {showFreeCreditsPopup && ( - + @@ -147,7 +143,7 @@ export default function LandingPage() { /> - + )} setVideoUrl( - Platform.OS === "web" - ? video?.landingWeb - : video?.landing, + Platform.OS === "web" ? video?.landingWeb : video?.landing ) } containerStyle={styles.actionButton} diff --git a/src/screens/Playback/RecordPlayback.js b/src/screens/Playback/RecordPlayback.js index 8e6f03c..b498490 100644 --- a/src/screens/Playback/RecordPlayback.js +++ b/src/screens/Playback/RecordPlayback.js @@ -8,13 +8,7 @@ import React, { useRef, useState, } from "react"; -import { - AppState, - BackHandler, - Text, - TouchableOpacity, - View, -} from "react-native"; +import { BackHandler, Text, TouchableOpacity, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Svg, { Circle } from "react-native-svg"; import alert from "../../components/Alert"; @@ -62,7 +56,7 @@ const RecordPlayback = ({ route }) => { const isPausedRef = useRef(false); const stopRequestedRef = useRef(false); const restartRequestedRef = useRef(false); - const interruptedRef = useRef(false); // New ref to track interruptions + const recordingStartTimeRef = useRef(0); // Track recording start time for duration check const manualRestartInFlightRef = useRef(false); const stopRequestedAtRef = useRef(0); const activeRecordingPromiseRef = useRef(null); @@ -114,42 +108,8 @@ const RecordPlayback = ({ route }) => { isPausedRef.current = isPaused; }, [isPaused]); - // Monitor AppState to detect backgrounding/interruptions - useEffect(() => { - const subscription = AppState.addEventListener("change", (nextAppState) => { - if ( - nextAppState.match(/inactive|background/) && - (isRecording || isPreparing) - ) { - log("App backgrounded/interrupted during recording session"); - interruptedRef.current = true; - - // If we are merely preparing (countdown), we can just stop the timer - if (isPreparing) { - if (countdownTimerRef.current) { - clearInterval(countdownTimerRef.current); - countdownTimerRef.current = null; - } - setIsPreparing(false); - setCountdown(0); - } - - // If recording, request stop immediately. - // The recording promise will resolve, and we will check interruptedRef there. - if (isRecording) { - stopRequestedRef.current = true; - stopRequestedAtRef.current = Date.now(); - try { - cameraRef.current?.stopRecording?.(); - } catch (_) {} - } - } - }); - - return () => { - subscription.remove(); - }; - }, [isRecording, isPreparing]); + // Removed AppState listener as it was too aggressive. + // We now use duration validation at the end of recording. const musicIndex = useMemo(() => { const i = Number(songIndex); @@ -495,9 +455,7 @@ const RecordPlayback = ({ route }) => { try { stopRequestedRef.current = false; stopRequestedAtRef.current = 0; - stopRequestedRef.current = false; - stopRequestedAtRef.current = 0; - interruptedRef.current = false; // Reset interruption flag + recordingStartTimeRef.current = Date.now(); // Start timer listenedMsRef.current = 0; incrementDoneRef.current = false; @@ -679,6 +637,7 @@ const RecordPlayback = ({ route }) => { } const video = await recordPromise; + const recordEndTime = Date.now(); log("Recording promise resolved", { hasVideo: !!video?.uri }); activeRecordingPromiseRef.current = null; stopRequestedRef.current = false; @@ -707,21 +666,32 @@ const RecordPlayback = ({ route }) => { progressLogRef.current = { bucket: -1, lastPos: -1, lastDur: -1 }; const exitRequested = exitRequestedRef.current; const shouldRestart = restartRequestedRef.current; - const wasInterrupted = interruptedRef.current; + // Duration check logic + const recordedDurationMs = recordEndTime - recordingStartTimeRef.current; + const expectedDurationMs = (player?.duration || 0) * 1000; - if (wasInterrupted) { - log( - "Recording was interrupted (background/inactive), discarding and exiting" - ); - exitRequestedRef.current = false; - restartRequestedRef.current = false; - manualRestartInFlightRef.current = false; - interruptedRef.current = false; - await discardRecordingFile(video?.uri, "interrupted"); - try { - goBack(); - } catch (_) {} - return; + if (!exitRequested && !shouldRestart) { + // If the app was backgrounded/interrupted, the camera stops early, + // resulting in a duration significantly shorter than the song. + // We only check this if we have a valid song duration. + // Tolerance: 1.5s + if ( + expectedDurationMs > 0 && + recordedDurationMs < expectedDurationMs - 1500 + ) { + log( + "Recording too short compared to song duration - likely interrupted", + { + recordedDurationMs, + expectedDurationMs, + } + ); + await discardRecordingFile(video?.uri, "too_short"); + try { + goBack(); + } catch (_) {} + return; + } } if (exitRequested) { @@ -1140,7 +1110,7 @@ const RecordPlayback = ({ route }) => { position: "absolute", left: 0, right: 0, - bottom: bottom + 130, + bottom: bottom + 200, paddingHorizontal: gutters, }} >