mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Add TypeScript support for React components
This commit is contained in:
parent
0dde17fbd7
commit
4d23e79f65
16 changed files with 810 additions and 107 deletions
|
@ -1,6 +1,6 @@
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
|
||||
import globalize from '../../scripts/globalize';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
@ -18,10 +18,17 @@ const CARD_OPTIONS = {
|
|||
showChannelName: true
|
||||
};
|
||||
|
||||
type LiveTVSearchResultsProps = {
|
||||
serverId: string;
|
||||
parentId: string;
|
||||
collectionType: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
/*
|
||||
* React component to display search result rows for live tv library search
|
||||
*/
|
||||
const LiveTVSearchResults = ({ serverId, parentId, collectionType, query }) => {
|
||||
const LiveTVSearchResults: FunctionComponent<LiveTVSearchResultsProps> = ({ serverId, parentId, collectionType, query }) => {
|
||||
const [ movies, setMovies ] = useState([]);
|
||||
const [ episodes, setEpisodes ] = useState([]);
|
||||
const [ sports, setSports ] = useState([]);
|
||||
|
@ -30,34 +37,32 @@ const LiveTVSearchResults = ({ serverId, parentId, collectionType, query }) => {
|
|||
const [ programs, setPrograms ] = useState([]);
|
||||
const [ channels, setChannels ] = useState([]);
|
||||
|
||||
const getDefaultParameters = () => ({
|
||||
ParentId: parentId,
|
||||
searchTerm: query,
|
||||
Limit: 24,
|
||||
Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount',
|
||||
Recursive: true,
|
||||
EnableTotalRecordCount: false,
|
||||
ImageTypeLimit: 1,
|
||||
IncludePeople: false,
|
||||
IncludeMedia: false,
|
||||
IncludeGenres: false,
|
||||
IncludeStudios: false,
|
||||
IncludeArtists: false
|
||||
});
|
||||
|
||||
// FIXME: This query does not support Live TV filters
|
||||
const fetchItems = (apiClient, params = {}) => apiClient?.getItems(
|
||||
apiClient?.getCurrentUserId(),
|
||||
{
|
||||
...getDefaultParameters(),
|
||||
IncludeMedia: true,
|
||||
...params
|
||||
}
|
||||
);
|
||||
|
||||
const isLiveTV = () => collectionType === 'livetv';
|
||||
|
||||
useEffect(() => {
|
||||
const getDefaultParameters = () => ({
|
||||
ParentId: parentId,
|
||||
searchTerm: query,
|
||||
Limit: 24,
|
||||
Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount',
|
||||
Recursive: true,
|
||||
EnableTotalRecordCount: false,
|
||||
ImageTypeLimit: 1,
|
||||
IncludePeople: false,
|
||||
IncludeMedia: false,
|
||||
IncludeGenres: false,
|
||||
IncludeStudios: false,
|
||||
IncludeArtists: false
|
||||
});
|
||||
|
||||
// FIXME: This query does not support Live TV filters
|
||||
const fetchItems = (apiClient, params = {}) => apiClient?.getItems(
|
||||
apiClient?.getCurrentUserId(),
|
||||
{
|
||||
...getDefaultParameters(),
|
||||
IncludeMedia: true,
|
||||
...params
|
||||
}
|
||||
);
|
||||
|
||||
// Reset state
|
||||
setMovies([]);
|
||||
setEpisodes([]);
|
||||
|
@ -67,8 +72,9 @@ const LiveTVSearchResults = ({ serverId, parentId, collectionType, query }) => {
|
|||
setPrograms([]);
|
||||
setChannels([]);
|
||||
|
||||
if (query && isLiveTV()) {
|
||||
const apiClient = ServerConnections.getApiClient(serverId);
|
||||
if (query && collectionType === 'livetv') {
|
||||
// TODO: Remove type casting once we're using a properly typed API client
|
||||
const apiClient = (ServerConnections as any).getApiClient(serverId);
|
||||
|
||||
// Movies row
|
||||
fetchItems(apiClient, {
|
||||
|
@ -128,7 +134,7 @@ const LiveTVSearchResults = ({ serverId, parentId, collectionType, query }) => {
|
|||
fetchItems(apiClient, { IncludeItemTypes: 'TvChannel' })
|
||||
.then(result => setChannels(result.Items));
|
||||
}
|
||||
}, [ query ]);
|
||||
}, [collectionType, parentId, query, serverId]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -136,7 +142,7 @@ const LiveTVSearchResults = ({ serverId, parentId, collectionType, query }) => {
|
|||
'searchResults',
|
||||
'padded-bottom-page',
|
||||
'padded-top',
|
||||
{ 'hide': !query || !isLiveTV() }
|
||||
{ 'hide': !query || !(collectionType === 'livetv') }
|
||||
)}
|
||||
>
|
||||
<SearchResultsRow
|
|
@ -1,6 +1,6 @@
|
|||
import debounce from 'lodash-es/debounce';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
import React, { FunctionComponent, useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
import AlphaPicker from '../alphaPicker/AlphaPickerComponent';
|
||||
import globalize from '../../scripts/globalize';
|
||||
|
@ -31,12 +31,17 @@ const createInputElement = () => ({
|
|||
|
||||
const normalizeInput = (value = '') => value.trim();
|
||||
|
||||
const SearchFields = ({ onSearch = () => {} }) => {
|
||||
type SearchFieldsProps = {
|
||||
onSearch: () => void
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
const SearchFields: FunctionComponent<SearchFieldsProps> = ({ onSearch = () => {} }) => {
|
||||
const element = useRef(null);
|
||||
|
||||
const getSearchInput = () => element?.current?.querySelector('.searchfields-txtSearch');
|
||||
|
||||
const debouncedOnSearch = useMemo(() => debounce(onSearch, 400), []);
|
||||
const debouncedOnSearch = useMemo(() => debounce(onSearch, 400), [onSearch]);
|
||||
|
||||
useEffect(() => {
|
||||
getSearchInput()?.addEventListener('input', e => {
|
||||
|
@ -47,7 +52,7 @@ const SearchFields = ({ onSearch = () => {} }) => {
|
|||
return () => {
|
||||
debouncedOnSearch.cancel();
|
||||
};
|
||||
}, []);
|
||||
}, [debouncedOnSearch]);
|
||||
|
||||
const onAlphaPicked = e => {
|
||||
const value = e.detail.value;
|
|
@ -1,15 +1,22 @@
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
|
||||
import globalize from '../../scripts/globalize';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import SearchResultsRow from './SearchResultsRow';
|
||||
|
||||
type SearchResultsProps = {
|
||||
serverId: string;
|
||||
parentId: string;
|
||||
collectionType: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
/*
|
||||
* React component to display search result rows for global search and non-live tv library search
|
||||
*/
|
||||
const SearchResults = ({ serverId, parentId, collectionType, query }) => {
|
||||
const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId, parentId, collectionType, query }) => {
|
||||
const [ movies, setMovies ] = useState([]);
|
||||
const [ shows, setShows ] = useState([]);
|
||||
const [ episodes, setEpisodes ] = useState([]);
|
||||
|
@ -26,55 +33,55 @@ const SearchResults = ({ serverId, parentId, collectionType, query }) => {
|
|||
const [ books, setBooks ] = useState([]);
|
||||
const [ people, setPeople ] = useState([]);
|
||||
|
||||
const getDefaultParameters = () => ({
|
||||
ParentId: parentId,
|
||||
searchTerm: query,
|
||||
Limit: 24,
|
||||
Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount',
|
||||
Recursive: true,
|
||||
EnableTotalRecordCount: false,
|
||||
ImageTypeLimit: 1,
|
||||
IncludePeople: false,
|
||||
IncludeMedia: false,
|
||||
IncludeGenres: false,
|
||||
IncludeStudios: false,
|
||||
IncludeArtists: false
|
||||
});
|
||||
|
||||
const fetchArtists = (apiClient, params = {}) => apiClient?.getArtists(
|
||||
apiClient?.getCurrentUserId(),
|
||||
{
|
||||
...getDefaultParameters(),
|
||||
IncludeArtists: true,
|
||||
...params
|
||||
}
|
||||
);
|
||||
|
||||
const fetchItems = (apiClient, params = {}) => apiClient?.getItems(
|
||||
apiClient?.getCurrentUserId(),
|
||||
{
|
||||
...getDefaultParameters(),
|
||||
IncludeMedia: true,
|
||||
...params
|
||||
}
|
||||
);
|
||||
|
||||
const fetchPeople = (apiClient, params = {}) => apiClient?.getPeople(
|
||||
apiClient?.getCurrentUserId(),
|
||||
{
|
||||
...getDefaultParameters(),
|
||||
IncludePeople: true,
|
||||
...params
|
||||
}
|
||||
);
|
||||
|
||||
const isMovies = () => collectionType === 'movies';
|
||||
|
||||
const isMusic = () => collectionType === 'music';
|
||||
|
||||
const isTVShows = () => collectionType === 'tvshows' || collectionType === 'tv';
|
||||
|
||||
useEffect(() => {
|
||||
const getDefaultParameters = () => ({
|
||||
ParentId: parentId,
|
||||
searchTerm: query,
|
||||
Limit: 24,
|
||||
Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount',
|
||||
Recursive: true,
|
||||
EnableTotalRecordCount: false,
|
||||
ImageTypeLimit: 1,
|
||||
IncludePeople: false,
|
||||
IncludeMedia: false,
|
||||
IncludeGenres: false,
|
||||
IncludeStudios: false,
|
||||
IncludeArtists: false
|
||||
});
|
||||
|
||||
const fetchArtists = (apiClient, params = {}) => apiClient?.getArtists(
|
||||
apiClient?.getCurrentUserId(),
|
||||
{
|
||||
...getDefaultParameters(),
|
||||
IncludeArtists: true,
|
||||
...params
|
||||
}
|
||||
);
|
||||
|
||||
const fetchItems = (apiClient, params = {}) => apiClient?.getItems(
|
||||
apiClient?.getCurrentUserId(),
|
||||
{
|
||||
...getDefaultParameters(),
|
||||
IncludeMedia: true,
|
||||
...params
|
||||
}
|
||||
);
|
||||
|
||||
const fetchPeople = (apiClient, params = {}) => apiClient?.getPeople(
|
||||
apiClient?.getCurrentUserId(),
|
||||
{
|
||||
...getDefaultParameters(),
|
||||
IncludePeople: true,
|
||||
...params
|
||||
}
|
||||
);
|
||||
|
||||
const isMovies = () => collectionType === 'movies';
|
||||
|
||||
const isMusic = () => collectionType === 'music';
|
||||
|
||||
const isTVShows = () => collectionType === 'tvshows' || collectionType === 'tv';
|
||||
|
||||
// Reset state
|
||||
setMovies([]);
|
||||
setShows([]);
|
||||
|
@ -93,7 +100,8 @@ const SearchResults = ({ serverId, parentId, collectionType, query }) => {
|
|||
setPeople([]);
|
||||
|
||||
if (query) {
|
||||
const apiClient = ServerConnections.getApiClient(serverId);
|
||||
// TODO: Remove type casting once we're using a properly typed API client
|
||||
const apiClient = (ServerConnections as any).getApiClient(serverId);
|
||||
|
||||
// Movie libraries
|
||||
if (!collectionType || isMovies()) {
|
||||
|
@ -160,7 +168,7 @@ const SearchResults = ({ serverId, parentId, collectionType, query }) => {
|
|||
.then(results => setBooks(results.Items));
|
||||
}
|
||||
}
|
||||
}, [ query ]);
|
||||
}, [collectionType, parentId, query, serverId]);
|
||||
|
||||
return (
|
||||
<div
|
|
@ -1,5 +1,5 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import React, { FunctionComponent, useEffect, useRef } from 'react';
|
||||
|
||||
import cardBuilder from '../cardbuilder/cardBuilder';
|
||||
|
||||
|
@ -16,7 +16,13 @@ const createScroller = ({ title = '' }) => ({
|
|||
</div>`
|
||||
});
|
||||
|
||||
const SearchResultsRow = ({ title, items = [], cardOptions = {} }) => {
|
||||
type SearchResultsRowProps = {
|
||||
title: string;
|
||||
items: Array<any>; // TODO: Should be Array<BaseItemDto> once we have a typed API client
|
||||
cardOptions: Record<string, any>;
|
||||
}
|
||||
|
||||
const SearchResultsRow: FunctionComponent<SearchResultsRowProps> = ({ title, items = [], cardOptions = {} }) => {
|
||||
const element = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -31,7 +37,7 @@ const SearchResultsRow = ({ title, items = [], cardOptions = {} }) => {
|
|||
allowBottomPadding: false,
|
||||
...cardOptions
|
||||
});
|
||||
}, [ items ]);
|
||||
}, [cardOptions, items]);
|
||||
|
||||
return (
|
||||
<div
|
|
@ -1,5 +1,5 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
|
||||
import { appRouter } from '../appRouter';
|
||||
import globalize from '../../scripts/globalize';
|
||||
|
@ -19,11 +19,17 @@ const createSuggestionLink = ({name, href}) => ({
|
|||
>${name}</a>`
|
||||
});
|
||||
|
||||
const SearchSuggestions = ({ serverId, parentId }) => {
|
||||
type SearchSuggestionsProps = {
|
||||
serverId: string;
|
||||
parentId: string;
|
||||
}
|
||||
|
||||
const SearchSuggestions: FunctionComponent<SearchSuggestionsProps> = ({ serverId, parentId }) => {
|
||||
const [ suggestions, setSuggestions ] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const apiClient = ServerConnections.getApiClient(serverId);
|
||||
// TODO: Remove type casting once we're using a properly typed API client
|
||||
const apiClient = (ServerConnections as any).getApiClient(serverId);
|
||||
|
||||
apiClient.getItems(apiClient.getCurrentUserId(), {
|
||||
SortBy: 'IsFavoriteOrLiked,Random',
|
||||
|
@ -35,7 +41,7 @@ const SearchSuggestions = ({ serverId, parentId }) => {
|
|||
ParentId: parentId,
|
||||
EnableTotalRecordCount: false
|
||||
}).then(result => setSuggestions(result.Items));
|
||||
}, []);
|
||||
}, [parentId, serverId]);
|
||||
|
||||
return (
|
||||
<div
|
Loading…
Add table
Add a link
Reference in a new issue