1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge pull request #4824 from GGProGaming/Search

Resolve null searches with return text | Loading circle in search
This commit is contained in:
Bill Thornton 2023-10-24 13:11:24 -04:00 committed by GitHub
commit 167a13d974
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 204 additions and 154 deletions

View file

@ -70,6 +70,7 @@
- [tehciolo](https://github.com/tehciolo) - [tehciolo](https://github.com/tehciolo)
- [scampower3](https://github.com/scampower3) - [scampower3](https://github.com/scampower3)
- [LittleBigOwI] (https://github.com/LittleBigOwI/) - [LittleBigOwI] (https://github.com/LittleBigOwI/)
- [Nate G](https://github.com/GGProGaming)
# Emby Contributors # Emby Contributors

View file

@ -6,6 +6,7 @@ import React, { FunctionComponent, useCallback, useEffect, useState } from 'reac
import globalize from '../../scripts/globalize'; import globalize from '../../scripts/globalize';
import ServerConnections from '../ServerConnections'; import ServerConnections from '../ServerConnections';
import SearchResultsRow from './SearchResultsRow'; import SearchResultsRow from './SearchResultsRow';
import Loading from '../loading/LoadingComponent';
type SearchResultsProps = { type SearchResultsProps = {
serverId?: string; serverId?: string;
@ -45,6 +46,7 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
const [ books, setBooks ] = useState<BaseItemDto[]>([]); const [ books, setBooks ] = useState<BaseItemDto[]>([]);
const [ people, setPeople ] = useState<BaseItemDto[]>([]); const [ people, setPeople ] = useState<BaseItemDto[]>([]);
const [ collections, setCollections ] = useState<BaseItemDto[]>([]); const [ collections, setCollections ] = useState<BaseItemDto[]>([]);
const [isLoading, setIsLoading] = useState(false);
const getDefaultParameters = useCallback(() => ({ const getDefaultParameters = useCallback(() => ({
ParentId: parentId, ParentId: parentId,
@ -114,99 +116,123 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
setCollections([]); setCollections([]);
if (!query) { if (!query) {
setIsLoading(false);
return; return;
} }
setIsLoading(true);
const apiClient = ServerConnections.getApiClient(serverId); const apiClient = ServerConnections.getApiClient(serverId);
const fetchPromises = [];
// Movie libraries // Movie libraries
if (!collectionType || isMovies(collectionType)) { if (!collectionType || isMovies(collectionType)) {
// Movies row fetchPromises.push(
fetchItems(apiClient, { IncludeItemTypes: 'Movie' }) // Movies row
.then(result => setMovies(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'Movie' })
.catch(() => setMovies([])); .then(result => setMovies(result.Items))
.catch(() => setMovies([]))
);
} }
// TV Show libraries // TV Show libraries
if (!collectionType || isTVShows(collectionType)) { if (!collectionType || isTVShows(collectionType)) {
// Shows row fetchPromises.push(
fetchItems(apiClient, { IncludeItemTypes: 'Series' }) // Shows row
.then(result => setShows(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'Series' })
.catch(() => setShows([])); .then(result => setShows(result.Items))
// Episodes row .catch(() => setShows([])),
fetchItems(apiClient, { IncludeItemTypes: 'Episode' }) // Episodes row
.then(result => setEpisodes(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'Episode' })
.catch(() => setEpisodes([])); .then(result => setEpisodes(result.Items))
.catch(() => setEpisodes([]))
);
} }
// People are included for Movies and TV Shows // People are included for Movies and TV Shows
if (!collectionType || isMovies(collectionType) || isTVShows(collectionType)) { if (!collectionType || isMovies(collectionType) || isTVShows(collectionType)) {
// People row fetchPromises.push(
fetchPeople(apiClient) // People row
.then(result => setPeople(result.Items)) fetchPeople(apiClient)
.catch(() => setPeople([])); .then(result => setPeople(result.Items))
.catch(() => setPeople([]))
);
} }
// Music libraries // Music libraries
if (!collectionType || isMusic(collectionType)) { if (!collectionType || isMusic(collectionType)) {
// Playlists row fetchPromises.push(
fetchItems(apiClient, { IncludeItemTypes: 'Playlist' }) // Playlists row
.then(results => setPlaylists(results.Items)) fetchItems(apiClient, { IncludeItemTypes: 'Playlist' })
.catch(() => setPlaylists([])); .then(results => setPlaylists(results.Items))
// Artists row .catch(() => setPlaylists([])),
fetchArtists(apiClient) // Artists row
.then(result => setArtists(result.Items)) fetchArtists(apiClient)
.catch(() => setArtists([])); .then(result => setArtists(result.Items))
// Albums row .catch(() => setArtists([])),
fetchItems(apiClient, { IncludeItemTypes: 'MusicAlbum' }) // Albums row
.then(result => setAlbums(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'MusicAlbum' })
.catch(() => setAlbums([])); .then(result => setAlbums(result.Items))
// Songs row .catch(() => setAlbums([])),
fetchItems(apiClient, { IncludeItemTypes: 'Audio' }) // Songs row
.then(result => setSongs(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'Audio' })
.catch(() => setSongs([])); .then(result => setSongs(result.Items))
.catch(() => setSongs([]))
);
} }
// Other libraries do not support in-library search currently // Other libraries do not support in-library search currently
if (!collectionType) { if (!collectionType) {
// Videos row fetchPromises.push(
fetchItems(apiClient, { // Videos row
MediaTypes: 'Video', fetchItems(apiClient, {
ExcludeItemTypes: 'Movie,Episode,TvChannel' MediaTypes: 'Video',
}) ExcludeItemTypes: 'Movie,Episode,TvChannel'
.then(result => setVideos(result.Items)) })
.catch(() => setVideos([])); .then(result => setVideos(result.Items))
// Programs row .catch(() => setVideos([])),
fetchItems(apiClient, { IncludeItemTypes: 'LiveTvProgram' }) // Programs row
.then(result => setPrograms(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'LiveTvProgram' })
.catch(() => setPrograms([])); .then(result => setPrograms(result.Items))
// Channels row .catch(() => setPrograms([])),
fetchItems(apiClient, { IncludeItemTypes: 'TvChannel' }) // Channels row
.then(result => setChannels(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'TvChannel' })
.catch(() => setChannels([])); .then(result => setChannels(result.Items))
// Photo Albums row .catch(() => setChannels([])),
fetchItems(apiClient, { IncludeItemTypes: 'PhotoAlbum' }) // Photo Albums row
.then(result => setPhotoAlbums(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'PhotoAlbum' })
.catch(() => setPhotoAlbums([])); .then(result => setPhotoAlbums(result.Items))
// Photos row .catch(() => setPhotoAlbums([])),
fetchItems(apiClient, { IncludeItemTypes: 'Photo' }) // Photos row
.then(result => setPhotos(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'Photo' })
.catch(() => setPhotos([])); .then(result => setPhotos(result.Items))
// Audio Books row .catch(() => setPhotos([])),
fetchItems(apiClient, { IncludeItemTypes: 'AudioBook' }) // Audio Books row
.then(result => setAudioBooks(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'AudioBook' })
.catch(() => setAudioBooks([])); .then(result => setAudioBooks(result.Items))
// Books row .catch(() => setAudioBooks([])),
fetchItems(apiClient, { IncludeItemTypes: 'Book' }) // Books row
.then(result => setBooks(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'Book' })
.catch(() => setBooks([])); .then(result => setBooks(result.Items))
// Collections row .catch(() => setBooks([])),
fetchItems(apiClient, { IncludeItemTypes: 'BoxSet' }) // Collections row
.then(result => setCollections(result.Items)) fetchItems(apiClient, { IncludeItemTypes: 'BoxSet' })
.catch(() => setCollections([])); .then(result => setCollections(result.Items))
.catch(() => setCollections([]))
);
} }
Promise.all(fetchPromises)
.then(() => {
setIsLoading(false); // Set loading to false when all fetch calls are done
})
.catch((error) => {
console.error('An error occurred while fetching data:', error);
setIsLoading(false); // Set loading to false even if an error occurs
});
}, [collectionType, fetchArtists, fetchItems, fetchPeople, query, serverId]); }, [collectionType, fetchArtists, fetchItems, fetchPeople, query, serverId]);
const allEmpty = [movies, shows, episodes, videos, programs, channels, playlists, artists, albums, songs, photoAlbums, photos, audioBooks, books, people, collections].every(arr => arr.length === 0);
return ( return (
<div <div
className={classNames( className={classNames(
@ -216,93 +242,104 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
{ 'hide': !query || collectionType === 'livetv' } { 'hide': !query || collectionType === 'livetv' }
)} )}
> >
<SearchResultsRow {isLoading ? (
title={globalize.translate('Movies')} <Loading />
items={movies} ) : (
cardOptions={{ showYear: true }} <>
/> <SearchResultsRow
<SearchResultsRow title={globalize.translate('Movies')}
title={globalize.translate('Shows')} items={movies}
items={shows} cardOptions={{ showYear: true }}
cardOptions={{ showYear: true }} />
/> <SearchResultsRow
<SearchResultsRow title={globalize.translate('Shows')}
title={globalize.translate('Episodes')} items={shows}
items={episodes} cardOptions={{ showYear: true }}
cardOptions={{ />
coverImage: true, <SearchResultsRow
showParentTitle: true title={globalize.translate('Episodes')}
}} items={episodes}
/> cardOptions={{
<SearchResultsRow coverImage: true,
title={globalize.translate('HeaderVideos')} showParentTitle: true
items={videos} }}
cardOptions={{ showParentTitle: true }} />
/> <SearchResultsRow
<SearchResultsRow title={globalize.translate('HeaderVideos')}
title={globalize.translate('Programs')} items={videos}
items={programs} cardOptions={{ showParentTitle: true }}
cardOptions={{ />
preferThumb: true, <SearchResultsRow
inheritThumb: false, title={globalize.translate('Programs')}
showParentTitleOrTitle: true, items={programs}
showTitle: false, cardOptions={{
coverImage: true, preferThumb: true,
overlayMoreButton: true, inheritThumb: false,
showAirTime: true, showParentTitleOrTitle: true,
showAirDateTime: true, showTitle: false,
showChannelName: true coverImage: true,
}} overlayMoreButton: true,
/> showAirTime: true,
<SearchResultsRow showAirDateTime: true,
title={globalize.translate('Channels')} showChannelName: true
items={channels} }}
cardOptions={{ shape: 'square' }} />
/> <SearchResultsRow
<SearchResultsRow title={globalize.translate('Channels')}
title={globalize.translate('Playlists')} items={channels}
items={playlists} cardOptions={{ shape: 'square' }}
/> />
<SearchResultsRow <SearchResultsRow
title={globalize.translate('Artists')} title={globalize.translate('Playlists')}
items={artists} items={playlists}
cardOptions={{ coverImage: true }} />
/> <SearchResultsRow
<SearchResultsRow title={globalize.translate('Artists')}
title={globalize.translate('Albums')} items={artists}
items={albums} cardOptions={{ coverImage: true }}
cardOptions={{ showParentTitle: true }} />
/> <SearchResultsRow
<SearchResultsRow title={globalize.translate('Albums')}
title={globalize.translate('Songs')} items={albums}
items={songs} cardOptions={{ showParentTitle: true }}
cardOptions={{ showParentTitle: true }} />
/> <SearchResultsRow
<SearchResultsRow title={globalize.translate('Songs')}
title={globalize.translate('HeaderPhotoAlbums')} items={songs}
items={photoAlbums} cardOptions={{ showParentTitle: true }}
/> />
<SearchResultsRow <SearchResultsRow
title={globalize.translate('Photos')} title={globalize.translate('HeaderPhotoAlbums')}
items={photos} items={photoAlbums}
/> />
<SearchResultsRow <SearchResultsRow
title={globalize.translate('HeaderAudioBooks')} title={globalize.translate('Photos')}
items={audioBooks} items={photos}
/> />
<SearchResultsRow <SearchResultsRow
title={globalize.translate('Books')} title={globalize.translate('HeaderAudioBooks')}
items={books} items={audioBooks}
/> />
<SearchResultsRow <SearchResultsRow
title={globalize.translate('Collections')} title={globalize.translate('Books')}
items={collections} items={books}
/> />
<SearchResultsRow <SearchResultsRow
title={globalize.translate('People')} title={globalize.translate('Collections')}
items={people} items={collections}
cardOptions={{ coverImage: true }} />
/> <SearchResultsRow
title={globalize.translate('People')}
items={people}
cardOptions={{ coverImage: true }}
/>
{allEmpty && query && !isLoading && (
<div className='sorry-text'>{globalize.translate('SearchResultsEmpty', query)}</div>
)}
</>
)}
</div> </div>
); );
}; };

View file

@ -9,3 +9,14 @@
font-size: 2em; font-size: 2em;
align-self: flex-end; align-self: flex-end;
} }
.sorry-text {
font-size: 2em;
text-align: center;
font-family: inherit;
width: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

View file

@ -1428,6 +1428,7 @@
"SearchForMissingMetadata": "Search for missing metadata", "SearchForMissingMetadata": "Search for missing metadata",
"SearchForSubtitles": "Search for Subtitles", "SearchForSubtitles": "Search for Subtitles",
"SearchResults": "Search Results", "SearchResults": "Search Results",
"SearchResultsEmpty": "Sorry! No results found for \"{0}\"",
"Season": "Season", "Season": "Season",
"SecondarySubtitles": "Secondary Subtitles", "SecondarySubtitles": "Secondary Subtitles",
"SelectAdminUsername": "Please select a username for the admin account.", "SelectAdminUsername": "Please select a username for the admin account.",