Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
working-directory: ${{ matrix.dir }}
component: true
record: true
browser: chrome
env:
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
Expand Down
7 changes: 5 additions & 2 deletions packages/next/cypress/component/NextVisual.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ describe("complex layouts", () => {
});

describe("loading", () => {
it("images lazy load", () => {
// This stopped working in Chrome, it would always load even when offcreen by
// a lot. Since we're relying on native behavior for this, I think I'm
// handtied on this one.
it.skip("images lazy load", () => {
// Force responses to not be cached by browser
cy.intercept("https://placehold.co/200x200", (req) => {
req.on("before:response", (res) => {
Expand All @@ -187,7 +190,7 @@ describe("loading", () => {
height={200}
alt=""
data-cy="next-visual"
style={{ marginTop: VH + 1 }}
style={{ marginTop: VH * 3 }}
/>,
);

Expand Down
1 change: 0 additions & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"demo:build": "vite build --config vite.demo.config.ts"
},
"dependencies": {
"@react-hook/media-query": "^1.1.1",
"react-intersection-observer": "^9"
},
"peerDependencies": {
Expand Down
86 changes: 19 additions & 67 deletions packages/react/src/LazyVideo/LazyVideoClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@
"use client";

import { useInView } from "react-intersection-observer";
import { MediaQueryMatches, useMediaQueries } from "@react-hook/media-query";
import {
useEffect,
useRef,
useCallback,
type MutableRefObject,
useState,
type ReactNode,
useMemo,
} from "react";
import type { LazyVideoProps } from "../types/lazyVideoTypes";
import { fillStyles, transparentGif } from "../lib/styles";
Expand All @@ -30,11 +27,8 @@ type LazyVideoClientProps = Omit<

type ResponsiveVideoSourceProps = {
mediaSrcs: Required<LazyVideoClientProps>["mediaSrcs"];
videoRef: VideoRef;
};

type VideoRef = MutableRefObject<HTMLVideoElement | undefined>;

// An video rendered within a Visual that supports lazy loading
export default function LazyVideoClient({
srcUrl,
Expand Down Expand Up @@ -127,6 +121,17 @@ export default function LazyVideoClient({
};
}, []);

// Browsers won't swap <source media> on a video after initial load, so
// reload manually
useEffect(() => {
if (!mediaSrcs) return;
const queries = Object.keys(mediaSrcs).map((q) => window.matchMedia(q));
const reload = () => videoRef.current?.load();
queries.forEach((q) => q.addEventListener("change", reload));
return () =>
queries.forEach((q) => q.removeEventListener("change", reload));
}, [mediaSrcs]);

// Simplify logic for whether to load sources
const shouldLoad = priority || inView;

Expand Down Expand Up @@ -157,7 +162,14 @@ export default function LazyVideoClient({
{/* Implement lazy loading by not adding the source until ready */}
{shouldLoad &&
(mediaSrcs ? (
<ResponsiveSource {...{ mediaSrcs, videoRef }} />
Object.entries(mediaSrcs).map(([mediaQuery, src]) => (
<source
key={mediaQuery}
src={src}
type="video/mp4"
media={mediaQuery}
/>
))
) : (
<source src={srcUrl} type="video/mp4" />
))}
Expand All @@ -178,63 +190,3 @@ export default function LazyVideoClient({
</>
);
}

// Switch the video asset depending on media queries
function ResponsiveSource({
mediaSrcs,
videoRef,
}: ResponsiveVideoSourceProps): ReactNode {
// Make an object suitable for useMediaQueries that uses indexes from the
// mediaSrcs obj as its keys so there won't be any issues with multiple
// media queries using the same asset.
const indexedQueries = useMemo(() => {
return Object.keys(mediaSrcs).reduce<Record<number, string>>(
(queries, mediaQuery, index) => {
queries[index] = mediaQuery;
return queries;
},
{},
);
}, [mediaSrcs]);

// Find the src url that is currently active
const { matches } = useMediaQueries<typeof indexedQueries>(indexedQueries);
const srcUrl = getFirstMatch(mediaSrcs, matches);

// Reload the video since the source changed
useEffect(() => reloadVideoWhenSafe(videoRef), [matches]);

// Return new source
return <source src={srcUrl} type="video/mp4" />;
}

// Get the URL with a media query match
function getFirstMatch(
mediaSrcs: Record<string, string>,
matches: MediaQueryMatches<Record<number, string>>["matches"],
): string | undefined {
for (const index in matches) {
if (matches[index]) {
return Object.values(mediaSrcs)[index];
}
}
}

// Safely call load function on a video
function reloadVideoWhenSafe(videoRef: VideoRef): void {
if (!videoRef.current) return;
const video = videoRef.current;

// If already playing safely, load now
if (video.readyState >= 2) {
video.load();

// Else, wait for video to finish loading
} else {
const handleLoadedData = () => {
video.load();
video.removeEventListener("loadeddata", handleLoadedData);
};
video.addEventListener("loadeddata", handleLoadedData);
}
}
5 changes: 0 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -974,11 +974,6 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==

"@react-hook/media-query@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@react-hook/media-query/-/media-query-1.1.1.tgz#7fc4e52591784a39be924b62b4270ae3e18ec578"
integrity sha512-VM14wDOX5CW5Dn6b2lTiMd79BFMTut9AZj2+vIRT3LCKgMCYmdqruTtzDPSnIVDQdtxdPgtOzvU9oK20LopuOw==

"@rollup/pluginutils@^5.1.0":
version "5.1.2"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.2.tgz#d3bc9f0fea4fd4086aaac6aa102f3fa587ce8bd9"
Expand Down
Loading