mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-11-09 07:23:14 +00:00
Tighten React hook dependencies
This commit is contained in:
1 parent
0375b9c183
commit
bc2a5cc7af
5 files changed
+142
-123
No files matched your search
+1
-1
@@ -47,7 +47,7 @@ export default function App() {
|
||||
loadAllCustomFonts().catch((error) => {
|
||||
console.error("Failed to load custom fonts:", error);
|
||||
});
|
||||
}, []);
|
||||
}, [isMacOS]);
|
||||
|
||||
useEffect(() => {
|
||||
document.title =
|
||||
|
||||
@@ -42,7 +42,7 @@ export function MarqueeText({ text }: { text: string }) {
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const node = staticRef.current;
|
||||
if (!node) return;
|
||||
if (!node || node.textContent !== text) return;
|
||||
const checkOverflow = () => {
|
||||
setOverflowing(node.scrollWidth > node.clientWidth + 1);
|
||||
};
|
||||
@@ -81,7 +81,10 @@ export const SourceSelectorContent = ({
|
||||
selectedSource = "Screen",
|
||||
loading = false,
|
||||
onSourceSelect = () => undefined,
|
||||
}: Pick<SourceSelectorProps, "screenSources" | "windowSources" | "selectedSource" | "loading" | "onSourceSelect">) => {
|
||||
}: Pick<
|
||||
SourceSelectorProps,
|
||||
"screenSources" | "windowSources" | "selectedSource" | "loading" | "onSourceSelect"
|
||||
>) => {
|
||||
const t = useScopedT("launch");
|
||||
const renderSourceItem = (source: DesktopSource, index: number) => {
|
||||
const isSelected = selectedSource === source.name;
|
||||
@@ -116,12 +119,14 @@ export const SourceSelectorContent = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0 flex flex-col items-start text-left">
|
||||
<div className="flex-1 min-w-0 flex flex-col items-start text-left">
|
||||
<div className="text-sm font-medium source-selector-text w-full">
|
||||
<MarqueeText text={source.windowTitle || source.name} />
|
||||
</div>
|
||||
<div className="text-xs source-selector-subtle truncate w-full text-left">
|
||||
{source.sourceType === "screen" ? t("recording.screen") : t("recording.window")}
|
||||
{source.sourceType === "screen"
|
||||
? t("recording.screen")
|
||||
: t("recording.window")}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -156,7 +161,9 @@ export const SourceSelectorContent = ({
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-0.5">
|
||||
{screenSources.map((source, index) => renderSourceItem(source, index))}
|
||||
{screenSources.map((source, index) =>
|
||||
renderSourceItem(source, index),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -166,7 +173,9 @@ export const SourceSelectorContent = ({
|
||||
{t("recording.windows")}
|
||||
</div>
|
||||
<div className="space-y-0.5">
|
||||
{windowSources.map((source, index) => renderSourceItem(source, index))}
|
||||
{windowSources.map((source, index) =>
|
||||
renderSourceItem(source, index),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -1260,7 +1260,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
const nextVolume = Math.max(0, Math.min(1, volume));
|
||||
video.volume = nextVolume;
|
||||
video.muted = nextVolume <= 0.001;
|
||||
}, [volume, videoPath]);
|
||||
}, [volume]);
|
||||
|
||||
useEffect(() => {
|
||||
layoutVideoContentRef.current = layoutVideoContent;
|
||||
@@ -1362,12 +1362,12 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [aspectRatio, borderRadius, cropRegion, frame, frameUpdateCounter, padding]);
|
||||
}, [frame, frameUpdateCounter]);
|
||||
|
||||
// Always re-run geometric layout when layout props change, even if frame sprite isn't reloaded.
|
||||
useEffect(() => {
|
||||
layoutVideoContentRef.current?.();
|
||||
}, [aspectRatio, borderRadius, cropRegion, padding]);
|
||||
layoutVideoContent();
|
||||
}, [layoutVideoContent]);
|
||||
|
||||
const selectedZoom = useMemo(() => {
|
||||
if (!selectedZoomId) return null;
|
||||
@@ -1545,6 +1545,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
|
||||
useEffect(() => {
|
||||
suspendRenderingRef.current = suspendRendering;
|
||||
if (!pixiReady) return;
|
||||
const app = appRef.current;
|
||||
if (!app?.ticker) {
|
||||
return;
|
||||
@@ -1656,6 +1657,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
}, [speedRegions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pixiReady) return;
|
||||
const videoEffectsContainer = videoEffectsContainerRef.current;
|
||||
const zoomBlurFilter = zoomBlurFilterRef.current;
|
||||
const motionBlurFilter = motionBlurFilterRef.current;
|
||||
@@ -1783,7 +1785,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
motionBlurStateRef.current = createMotionBlurState();
|
||||
videoEffectsContainer.filters =
|
||||
zoomMotionBlur > 0 ? [motionBlurFilter, zoomBlurFilter] : null;
|
||||
}, [videoPath, zoomMotionBlur]);
|
||||
}, [zoomMotionBlur]);
|
||||
|
||||
useEffect(() => {
|
||||
zoomMotionBlurTuningRef.current = zoomMotionBlurTuning;
|
||||
@@ -1905,7 +1907,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [pixiReady, videoReady, layoutVideoContent, cropRegion]);
|
||||
}, [pixiReady, videoReady, layoutVideoContent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pixiReady || !videoReady) return;
|
||||
@@ -2013,11 +2015,13 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
syncWebcamMedia();
|
||||
}, [syncWebcamMedia]);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: The media path intentionally triggers source-specific state reset.
|
||||
useEffect(() => {
|
||||
setWebcamVideoDimensions(null);
|
||||
lastWebcamSyncTimeRef.current = null;
|
||||
}, [webcamVideoPath]);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: The wallpaper identity intentionally resets media synchronization.
|
||||
useEffect(() => {
|
||||
lastBackgroundSyncTimeRef.current = null;
|
||||
}, [wallpaper]);
|
||||
@@ -2171,8 +2175,9 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
cursorContainerRef.current = null;
|
||||
videoSpriteRef.current = null;
|
||||
};
|
||||
}, [initializePixiRenderer, onError]);
|
||||
}, [initializePixiRenderer, onError, syncPreviewMotionBlurQuality]);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: A new media path must reset the persistent video element.
|
||||
useEffect(() => {
|
||||
const video = videoRef.current;
|
||||
if (!video) return;
|
||||
@@ -2273,7 +2278,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
|
||||
videoSpriteRef.current = null;
|
||||
};
|
||||
}, [pixiReady, videoReady, onTimeUpdate, updateOverlayForRegion]);
|
||||
}, [layoutVideoContent, onPlayStateChange, onTimeUpdate, pixiReady, videoReady]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pixiReady || !videoReady) return;
|
||||
@@ -2650,7 +2655,6 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
}, [
|
||||
pixiReady,
|
||||
videoReady,
|
||||
clampFocusToStage,
|
||||
applyWebcamBubbleLayout,
|
||||
borderRadius,
|
||||
padding,
|
||||
|
||||
@@ -5,110 +5,115 @@ import type {
|
||||
} from "@/components/video-editor/audio/audioTypes";
|
||||
|
||||
interface UseSourceAudioTrackSettingsParams {
|
||||
selectedClipId: string | null;
|
||||
activeClipId: string | null;
|
||||
sourceAudioTrackSettingsByClip: Record<string, SourceAudioTrackSettings>;
|
||||
setSourceAudioTrackSettingsByClip: React.Dispatch<
|
||||
React.SetStateAction<Record<string, SourceAudioTrackSettings>>
|
||||
>;
|
||||
defaultSourceAudioTrackSettings: SourceAudioTrackSettings;
|
||||
setDefaultSourceAudioTrackSettings: React.Dispatch<React.SetStateAction<SourceAudioTrackSettings>>;
|
||||
selectedClipId: string | null;
|
||||
activeClipId: string | null;
|
||||
sourceAudioTrackSettingsByClip: Record<string, SourceAudioTrackSettings>;
|
||||
setSourceAudioTrackSettingsByClip: React.Dispatch<
|
||||
React.SetStateAction<Record<string, SourceAudioTrackSettings>>
|
||||
>;
|
||||
defaultSourceAudioTrackSettings: SourceAudioTrackSettings;
|
||||
setDefaultSourceAudioTrackSettings: React.Dispatch<
|
||||
React.SetStateAction<SourceAudioTrackSettings>
|
||||
>;
|
||||
}
|
||||
|
||||
export interface UseSourceAudioTrackSettingsResult {
|
||||
sourceAudioTrackMeta: SourceAudioTrackMeta;
|
||||
activeSourceAudioTrackSettings: SourceAudioTrackSettings;
|
||||
selectedClipSourceAudioTrackSettings: SourceAudioTrackSettings;
|
||||
getSourceAudioTrackSettingsForClip: (clipId: string | null) => SourceAudioTrackSettings;
|
||||
onSourceAudioTracksMetaChange: (tracks: SourceAudioTrackMeta) => void;
|
||||
onSelectedClipSourceAudioTrackVolumeChange: (id: string, volume: number) => void;
|
||||
onSelectedClipSourceAudioTrackNormalizeChange: (id: string, normalize: boolean) => void;
|
||||
sourceAudioTrackMeta: SourceAudioTrackMeta;
|
||||
activeSourceAudioTrackSettings: SourceAudioTrackSettings;
|
||||
selectedClipSourceAudioTrackSettings: SourceAudioTrackSettings;
|
||||
getSourceAudioTrackSettingsForClip: (clipId: string | null) => SourceAudioTrackSettings;
|
||||
onSourceAudioTracksMetaChange: (tracks: SourceAudioTrackMeta) => void;
|
||||
onSelectedClipSourceAudioTrackVolumeChange: (id: string, volume: number) => void;
|
||||
onSelectedClipSourceAudioTrackNormalizeChange: (id: string, normalize: boolean) => void;
|
||||
}
|
||||
|
||||
function isSameTrackMeta(left: SourceAudioTrackMeta, right: SourceAudioTrackMeta): boolean {
|
||||
if (left.length !== right.length) return false;
|
||||
for (let index = 0; index < left.length; index += 1) {
|
||||
const leftTrack = left[index];
|
||||
const rightTrack = right[index];
|
||||
if (!leftTrack || !rightTrack) return false;
|
||||
if (leftTrack.id !== rightTrack.id || leftTrack.label !== rightTrack.label) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
if (left.length !== right.length) return false;
|
||||
for (let index = 0; index < left.length; index += 1) {
|
||||
const leftTrack = left[index];
|
||||
const rightTrack = right[index];
|
||||
if (!leftTrack || !rightTrack) return false;
|
||||
if (leftTrack.id !== rightTrack.id || leftTrack.label !== rightTrack.label) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function useSourceAudioTrackSettings({
|
||||
selectedClipId,
|
||||
activeClipId,
|
||||
sourceAudioTrackSettingsByClip,
|
||||
setSourceAudioTrackSettingsByClip,
|
||||
defaultSourceAudioTrackSettings,
|
||||
setDefaultSourceAudioTrackSettings,
|
||||
selectedClipId,
|
||||
activeClipId,
|
||||
sourceAudioTrackSettingsByClip,
|
||||
setSourceAudioTrackSettingsByClip,
|
||||
defaultSourceAudioTrackSettings,
|
||||
setDefaultSourceAudioTrackSettings,
|
||||
}: UseSourceAudioTrackSettingsParams): UseSourceAudioTrackSettingsResult {
|
||||
const [sourceAudioTrackMeta, setSourceAudioTrackMeta] = useState<SourceAudioTrackMeta>([]);
|
||||
const [sourceAudioTrackMeta, setSourceAudioTrackMeta] = useState<SourceAudioTrackMeta>([]);
|
||||
|
||||
const activeSourceAudioTrackSettings = useMemo(() => {
|
||||
if (!activeClipId) {
|
||||
return defaultSourceAudioTrackSettings;
|
||||
}
|
||||
return {
|
||||
...defaultSourceAudioTrackSettings,
|
||||
...(sourceAudioTrackSettingsByClip[activeClipId] ?? {}),
|
||||
};
|
||||
}, [activeClipId, defaultSourceAudioTrackSettings, sourceAudioTrackSettingsByClip]);
|
||||
const activeSourceAudioTrackSettings = useMemo(() => {
|
||||
if (!activeClipId) {
|
||||
return defaultSourceAudioTrackSettings;
|
||||
}
|
||||
return {
|
||||
...defaultSourceAudioTrackSettings,
|
||||
...(sourceAudioTrackSettingsByClip[activeClipId] ?? {}),
|
||||
};
|
||||
}, [activeClipId, defaultSourceAudioTrackSettings, sourceAudioTrackSettingsByClip]);
|
||||
|
||||
const selectedClipSourceAudioTrackSettings = useMemo(() => {
|
||||
if (!selectedClipId) {
|
||||
return defaultSourceAudioTrackSettings;
|
||||
}
|
||||
return {
|
||||
...defaultSourceAudioTrackSettings,
|
||||
...(sourceAudioTrackSettingsByClip[selectedClipId] ?? {}),
|
||||
};
|
||||
}, [defaultSourceAudioTrackSettings, selectedClipId, sourceAudioTrackSettingsByClip]);
|
||||
const selectedClipSourceAudioTrackSettings = useMemo(() => {
|
||||
if (!selectedClipId) {
|
||||
return defaultSourceAudioTrackSettings;
|
||||
}
|
||||
return {
|
||||
...defaultSourceAudioTrackSettings,
|
||||
...(sourceAudioTrackSettingsByClip[selectedClipId] ?? {}),
|
||||
};
|
||||
}, [defaultSourceAudioTrackSettings, selectedClipId, sourceAudioTrackSettingsByClip]);
|
||||
|
||||
const onSourceAudioTracksMetaChange = useCallback((tracks: SourceAudioTrackMeta) => {
|
||||
setSourceAudioTrackMeta((prev) => (isSameTrackMeta(prev, tracks) ? prev : tracks));
|
||||
setDefaultSourceAudioTrackSettings((prev) => {
|
||||
const next: SourceAudioTrackSettings = {};
|
||||
for (const track of tracks) {
|
||||
next[track.id] = prev[track.id] ?? { volume: 1, normalize: false };
|
||||
}
|
||||
const prevKeys = Object.keys(prev);
|
||||
const nextKeys = Object.keys(next);
|
||||
if (prevKeys.length !== nextKeys.length) {
|
||||
return next;
|
||||
}
|
||||
for (const key of nextKeys) {
|
||||
const prevSetting = prev[key];
|
||||
const nextSetting = next[key];
|
||||
if (!prevSetting || !nextSetting) {
|
||||
return next;
|
||||
}
|
||||
if (
|
||||
prevSetting.volume !== nextSetting.volume ||
|
||||
prevSetting.normalize !== nextSetting.normalize
|
||||
) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
}, []);
|
||||
const onSourceAudioTracksMetaChange = useCallback(
|
||||
(tracks: SourceAudioTrackMeta) => {
|
||||
setSourceAudioTrackMeta((prev) => (isSameTrackMeta(prev, tracks) ? prev : tracks));
|
||||
setDefaultSourceAudioTrackSettings((prev) => {
|
||||
const next: SourceAudioTrackSettings = {};
|
||||
for (const track of tracks) {
|
||||
next[track.id] = prev[track.id] ?? { volume: 1, normalize: false };
|
||||
}
|
||||
const prevKeys = Object.keys(prev);
|
||||
const nextKeys = Object.keys(next);
|
||||
if (prevKeys.length !== nextKeys.length) {
|
||||
return next;
|
||||
}
|
||||
for (const key of nextKeys) {
|
||||
const prevSetting = prev[key];
|
||||
const nextSetting = next[key];
|
||||
if (!prevSetting || !nextSetting) {
|
||||
return next;
|
||||
}
|
||||
if (
|
||||
prevSetting.volume !== nextSetting.volume ||
|
||||
prevSetting.normalize !== nextSetting.normalize
|
||||
) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
},
|
||||
[setDefaultSourceAudioTrackSettings],
|
||||
);
|
||||
|
||||
const getSourceAudioTrackSettingsForClip = useCallback(
|
||||
(clipId: string | null): SourceAudioTrackSettings => {
|
||||
if (!clipId) {
|
||||
return defaultSourceAudioTrackSettings;
|
||||
}
|
||||
return {
|
||||
...defaultSourceAudioTrackSettings,
|
||||
...(sourceAudioTrackSettingsByClip[clipId] ?? {}),
|
||||
};
|
||||
},
|
||||
[defaultSourceAudioTrackSettings, sourceAudioTrackSettingsByClip],
|
||||
);
|
||||
const getSourceAudioTrackSettingsForClip = useCallback(
|
||||
(clipId: string | null): SourceAudioTrackSettings => {
|
||||
if (!clipId) {
|
||||
return defaultSourceAudioTrackSettings;
|
||||
}
|
||||
return {
|
||||
...defaultSourceAudioTrackSettings,
|
||||
...(sourceAudioTrackSettingsByClip[clipId] ?? {}),
|
||||
};
|
||||
},
|
||||
[defaultSourceAudioTrackSettings, sourceAudioTrackSettingsByClip],
|
||||
);
|
||||
|
||||
const onSelectedClipSourceAudioTrackVolumeChange = useCallback(
|
||||
(id: string, volume: number) => {
|
||||
@@ -137,7 +142,7 @@ export function useSourceAudioTrackSettings({
|
||||
};
|
||||
});
|
||||
},
|
||||
[defaultSourceAudioTrackSettings, selectedClipId],
|
||||
[defaultSourceAudioTrackSettings, selectedClipId, setSourceAudioTrackSettingsByClip],
|
||||
);
|
||||
|
||||
const onSelectedClipSourceAudioTrackNormalizeChange = useCallback(
|
||||
@@ -161,16 +166,16 @@ export function useSourceAudioTrackSettings({
|
||||
};
|
||||
});
|
||||
},
|
||||
[defaultSourceAudioTrackSettings, selectedClipId],
|
||||
[defaultSourceAudioTrackSettings, selectedClipId, setSourceAudioTrackSettingsByClip],
|
||||
);
|
||||
|
||||
return {
|
||||
sourceAudioTrackMeta,
|
||||
activeSourceAudioTrackSettings,
|
||||
selectedClipSourceAudioTrackSettings,
|
||||
getSourceAudioTrackSettingsForClip,
|
||||
onSourceAudioTracksMetaChange,
|
||||
onSelectedClipSourceAudioTrackVolumeChange,
|
||||
onSelectedClipSourceAudioTrackNormalizeChange,
|
||||
};
|
||||
return {
|
||||
sourceAudioTrackMeta,
|
||||
activeSourceAudioTrackSettings,
|
||||
selectedClipSourceAudioTrackSettings,
|
||||
getSourceAudioTrackSettingsForClip,
|
||||
onSourceAudioTracksMetaChange,
|
||||
onSelectedClipSourceAudioTrackVolumeChange,
|
||||
onSelectedClipSourceAudioTrackNormalizeChange,
|
||||
};
|
||||
}
|
||||
@@ -44,6 +44,7 @@ function AudioWaveformComponent({
|
||||
}
|
||||
}, []);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: resizeKey intentionally redraws the canvas after ResizeObserver notifications.
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
@@ -79,15 +80,15 @@ function AudioWaveformComponent({
|
||||
const visibleStartMs = segmentStartMs ?? range.start;
|
||||
const visibleEndMs = segmentEndMs ?? range.end;
|
||||
const visibleDurationMs = visibleEndMs - visibleStartMs;
|
||||
|
||||
|
||||
if (visibleDurationMs <= 0) return;
|
||||
|
||||
const midY = height / 2;
|
||||
ctx.beginPath();
|
||||
|
||||
|
||||
for (let px = 0; px < width; px++) {
|
||||
const t = visibleStartMs + (px / width) * visibleDurationMs;
|
||||
|
||||
|
||||
// If the timeline time is beyond the actual audio duration, we draw nothing (flat line)
|
||||
if (t < 0 || t > durationMs) continue;
|
||||
|
||||
@@ -95,12 +96,12 @@ function AudioWaveformComponent({
|
||||
const leftIndex = Math.floor(exactIndex);
|
||||
const rightIndex = Math.min(peakData.length - 1, leftIndex + 1);
|
||||
const mix = exactIndex - leftIndex;
|
||||
|
||||
|
||||
let amplitude = peakData[leftIndex] * (1 - mix) + peakData[rightIndex] * mix;
|
||||
|
||||
|
||||
if (normalize) amplitude = Math.sqrt(Math.max(0, amplitude));
|
||||
amplitude = Math.max(0, Math.min(1, amplitude * gain));
|
||||
|
||||
|
||||
const barHeight = amplitude * midY * 0.85;
|
||||
|
||||
ctx.moveTo(px, midY - barHeight);
|
||||
|
||||
Reference in new issue
Block a user