mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat(frontend): only load request/tmdb cards when in the browser view
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
<img src="https://i.imgur.com/TMoEG7g.png" alt="Overseerr">
|
<img src="https://i.imgur.com/TMoEG7g.png" alt="Overseerr">
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
<img src="https://github.com/sct/overseerr/workflows/Overseerr%20Release/badge.svg?branch=master" alt="Overseerr Release" />
|
||||||
<img src="https://github.com/sct/overseerr/workflows/Overseerr%20CI/badge.svg" alt="Overseerr CI">
|
<img src="https://github.com/sct/overseerr/workflows/Overseerr%20CI/badge.svg" alt="Overseerr CI">
|
||||||
<a href="https://discord.gg/ySfaEUcQ">
|
<a href="https://discord.gg/ySfaEUcQ">
|
||||||
<img src="https://img.shields.io/discord/783137440809746482" alt="Discord">
|
<img src="https://img.shields.io/discord/783137440809746482" alt="Discord">
|
||||||
|
@@ -37,6 +37,7 @@
|
|||||||
"pug": "^3.0.0",
|
"pug": "^3.0.0",
|
||||||
"react": "16.13.1",
|
"react": "16.13.1",
|
||||||
"react-dom": "16.13.1",
|
"react-dom": "16.13.1",
|
||||||
|
"react-intersection-observer": "^8.31.0",
|
||||||
"react-intl": "^5.8.5",
|
"react-intl": "^5.8.5",
|
||||||
"react-spring": "^8.0.27",
|
"react-spring": "^8.0.27",
|
||||||
"react-toast-notifications": "^2.4.0",
|
"react-toast-notifications": "^2.4.0",
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
|
import { useInView } from 'react-intersection-observer';
|
||||||
import type { MediaRequest } from '../../../server/entity/MediaRequest';
|
import type { MediaRequest } from '../../../server/entity/MediaRequest';
|
||||||
import type { TvDetails } from '../../../server/models/Tv';
|
import type { TvDetails } from '../../../server/models/Tv';
|
||||||
import type { MovieDetails } from '../../../server/models/Movie';
|
import type { MovieDetails } from '../../../server/models/Movie';
|
||||||
@@ -42,6 +43,9 @@ interface RequestCardProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const RequestCard: React.FC<RequestCardProps> = ({ request }) => {
|
const RequestCard: React.FC<RequestCardProps> = ({ request }) => {
|
||||||
|
const { ref, inView } = useInView({
|
||||||
|
triggerOnce: true,
|
||||||
|
});
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { hasPermission } = useUser();
|
const { hasPermission } = useUser();
|
||||||
const { locale } = useContext(LanguageContext);
|
const { locale } = useContext(LanguageContext);
|
||||||
@@ -50,7 +54,7 @@ const RequestCard: React.FC<RequestCardProps> = ({ request }) => {
|
|||||||
? `/api/v1/movie/${request.media.tmdbId}`
|
? `/api/v1/movie/${request.media.tmdbId}`
|
||||||
: `/api/v1/tv/${request.media.tmdbId}`;
|
: `/api/v1/tv/${request.media.tmdbId}`;
|
||||||
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
|
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
|
||||||
`${url}?language=${locale}`
|
inView ? `${url}?language=${locale}` : null
|
||||||
);
|
);
|
||||||
const { data: requestData, error: requestError, revalidate } = useSWR<
|
const { data: requestData, error: requestError, revalidate } = useSWR<
|
||||||
MediaRequest
|
MediaRequest
|
||||||
@@ -67,7 +71,11 @@ const RequestCard: React.FC<RequestCardProps> = ({ request }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!title && !error) {
|
if (!title && !error) {
|
||||||
return <RequestCardPlaceholder />;
|
return (
|
||||||
|
<div ref={ref}>
|
||||||
|
<RequestCardPlaceholder />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!requestData && !requestError) {
|
if (!requestData && !requestError) {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import { useInView } from 'react-intersection-observer';
|
||||||
import type { MediaRequest } from '../../../../server/entity/MediaRequest';
|
import type { MediaRequest } from '../../../../server/entity/MediaRequest';
|
||||||
import {
|
import {
|
||||||
useIntl,
|
useIntl,
|
||||||
@@ -36,6 +37,9 @@ interface RequestItemProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const RequestItem: React.FC<RequestItemProps> = ({ request, onDelete }) => {
|
const RequestItem: React.FC<RequestItemProps> = ({ request, onDelete }) => {
|
||||||
|
const { ref, inView } = useInView({
|
||||||
|
triggerOnce: true,
|
||||||
|
});
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { hasPermission } = useUser();
|
const { hasPermission } = useUser();
|
||||||
const { locale } = useContext(LanguageContext);
|
const { locale } = useContext(LanguageContext);
|
||||||
@@ -44,7 +48,7 @@ const RequestItem: React.FC<RequestItemProps> = ({ request, onDelete }) => {
|
|||||||
? `/api/v1/movie/${request.media.tmdbId}`
|
? `/api/v1/movie/${request.media.tmdbId}`
|
||||||
: `/api/v1/tv/${request.media.tmdbId}`;
|
: `/api/v1/tv/${request.media.tmdbId}`;
|
||||||
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
|
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
|
||||||
`${url}?language=${locale}`
|
inView ? `${url}?language=${locale}` : null
|
||||||
);
|
);
|
||||||
const { data: requestData, error: requestError, revalidate } = useSWR<
|
const { data: requestData, error: requestError, revalidate } = useSWR<
|
||||||
MediaRequest
|
MediaRequest
|
||||||
@@ -68,7 +72,7 @@ const RequestItem: React.FC<RequestItemProps> = ({ request, onDelete }) => {
|
|||||||
|
|
||||||
if (!title && !error) {
|
if (!title && !error) {
|
||||||
return (
|
return (
|
||||||
<tr className="w-full bg-gray-800 animate-pulse h-24">
|
<tr className="w-full bg-gray-800 animate-pulse h-24" ref={ref}>
|
||||||
<td colSpan={6}></td>
|
<td colSpan={6}></td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import { useInView } from 'react-intersection-observer';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import type { MovieDetails } from '../../../server/models/Movie';
|
import type { MovieDetails } from '../../../server/models/Movie';
|
||||||
import type { TvDetails } from '../../../server/models/Tv';
|
import type { TvDetails } from '../../../server/models/Tv';
|
||||||
@@ -15,15 +16,22 @@ const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TmdbTitleCard: React.FC<TmdbTitleCardProps> = ({ tmdbId, type }) => {
|
const TmdbTitleCard: React.FC<TmdbTitleCardProps> = ({ tmdbId, type }) => {
|
||||||
|
const { ref, inView } = useInView({
|
||||||
|
triggerOnce: true,
|
||||||
|
});
|
||||||
const { locale } = useContext(LanguageContext);
|
const { locale } = useContext(LanguageContext);
|
||||||
const url =
|
const url =
|
||||||
type === 'movie' ? `/api/v1/movie/${tmdbId}` : `/api/v1/tv/${tmdbId}`;
|
type === 'movie' ? `/api/v1/movie/${tmdbId}` : `/api/v1/tv/${tmdbId}`;
|
||||||
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
|
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
|
||||||
`${url}?language=${locale}`
|
inView ? `${url}?language=${locale}` : null
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!title && !error) {
|
if (!title && !error) {
|
||||||
return <TitleCard.Placeholder />;
|
return (
|
||||||
|
<div ref={ref}>
|
||||||
|
<TitleCard.Placeholder />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!title) {
|
if (!title) {
|
||||||
|
@@ -11160,6 +11160,11 @@ react-fast-compare@^2.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
||||||
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
|
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
|
||||||
|
|
||||||
|
react-intersection-observer@^8.31.0:
|
||||||
|
version "8.31.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-8.31.0.tgz#0ed21aaf93c4c0475b22b0ccaba6169076d01605"
|
||||||
|
integrity sha512-XraIC/tkrD9JtrmVA7ypEN1QIpKc52mXBH1u/bz/aicRLo8QQEJQAMUTb8mz4B6dqpPwyzgjrr7Ljv/2ACDtqw==
|
||||||
|
|
||||||
react-intl@^5.8.5:
|
react-intl@^5.8.5:
|
||||||
version "5.8.5"
|
version "5.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-5.8.5.tgz#bc5dfab259049830621e129b8bffb1ac33ef4124"
|
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-5.8.5.tgz#bc5dfab259049830621e129b8bffb1ac33ef4124"
|
||||||
|
Reference in New Issue
Block a user