From 2ffead179a29fd291564531dc7d01ff175e71b1b Mon Sep 17 00:00:00 2001 From: Nathan G Date: Sun, 1 Oct 2023 17:44:39 -0700 Subject: [PATCH 01/11] First iteration --- jellyfin-web.code-workspace | 8 + src/components/search/SearchResults.tsx | 343 +++++++++++++----------- 2 files changed, 197 insertions(+), 154 deletions(-) create mode 100644 jellyfin-web.code-workspace diff --git a/jellyfin-web.code-workspace b/jellyfin-web.code-workspace new file mode 100644 index 00000000000..876a1499c09 --- /dev/null +++ b/jellyfin-web.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx index f8e5a12fbd1..bb0ca208473 100644 --- a/src/components/search/SearchResults.tsx +++ b/src/components/search/SearchResults.tsx @@ -45,6 +45,7 @@ const SearchResults: FunctionComponent = ({ serverId = windo const [ books, setBooks ] = useState([]); const [ people, setPeople ] = useState([]); const [ collections, setCollections ] = useState([]); + const [isLoading, setIsLoading] = useState(false); const getDefaultParameters = useCallback(() => ({ ParentId: parentId, @@ -112,101 +113,124 @@ const SearchResults: FunctionComponent = ({ serverId = windo setBooks([]); setPeople([]); setCollections([]); + setIsLoading(true); if (!query) { + setIsLoading(false); return; } const apiClient = ServerConnections.getApiClient(serverId); + const fetchPromises = []; // Movie libraries if (!collectionType || isMovies(collectionType)) { - // Movies row - fetchItems(apiClient, { IncludeItemTypes: 'Movie' }) - .then(result => setMovies(result.Items)) - .catch(() => setMovies([])); + fetchPromises.push( + // Movies row + fetchItems(apiClient, { IncludeItemTypes: 'Movie' }) + .then(result => setMovies(result.Items)) + .catch(() => setMovies([])) + ); } // TV Show libraries if (!collectionType || isTVShows(collectionType)) { - // Shows row - fetchItems(apiClient, { IncludeItemTypes: 'Series' }) - .then(result => setShows(result.Items)) - .catch(() => setShows([])); - // Episodes row - fetchItems(apiClient, { IncludeItemTypes: 'Episode' }) - .then(result => setEpisodes(result.Items)) - .catch(() => setEpisodes([])); + fetchPromises.push( + // Shows row + fetchItems(apiClient, { IncludeItemTypes: 'Series' }) + .then(result => setShows(result.Items)) + .catch(() => setShows([])), + // Episodes row + fetchItems(apiClient, { IncludeItemTypes: 'Episode' }) + .then(result => setEpisodes(result.Items)) + .catch(() => setEpisodes([])) + ); } // People are included for Movies and TV Shows if (!collectionType || isMovies(collectionType) || isTVShows(collectionType)) { - // People row - fetchPeople(apiClient) - .then(result => setPeople(result.Items)) - .catch(() => setPeople([])); + fetchPromises.push( + // People row + fetchPeople(apiClient) + .then(result => setPeople(result.Items)) + .catch(() => setPeople([])) + ); } // Music libraries if (!collectionType || isMusic(collectionType)) { - // Playlists row - fetchItems(apiClient, { IncludeItemTypes: 'Playlist' }) - .then(results => setPlaylists(results.Items)) - .catch(() => setPlaylists([])); - // Artists row - fetchArtists(apiClient) - .then(result => setArtists(result.Items)) - .catch(() => setArtists([])); - // Albums row - fetchItems(apiClient, { IncludeItemTypes: 'MusicAlbum' }) - .then(result => setAlbums(result.Items)) - .catch(() => setAlbums([])); - // Songs row - fetchItems(apiClient, { IncludeItemTypes: 'Audio' }) - .then(result => setSongs(result.Items)) - .catch(() => setSongs([])); + fetchPromises.push( + // Playlists row + fetchItems(apiClient, { IncludeItemTypes: 'Playlist' }) + .then(results => setPlaylists(results.Items)) + .catch(() => setPlaylists([])), + // Artists row + fetchArtists(apiClient) + .then(result => setArtists(result.Items)) + .catch(() => setArtists([])), + // Albums row + fetchItems(apiClient, { IncludeItemTypes: 'MusicAlbum' }) + .then(result => setAlbums(result.Items)) + .catch(() => setAlbums([])), + // Songs row + fetchItems(apiClient, { IncludeItemTypes: 'Audio' }) + .then(result => setSongs(result.Items)) + .catch(() => setSongs([])) + ); } // Other libraries do not support in-library search currently if (!collectionType) { - // Videos row - fetchItems(apiClient, { - MediaTypes: 'Video', - ExcludeItemTypes: 'Movie,Episode,TvChannel' - }) - .then(result => setVideos(result.Items)) - .catch(() => setVideos([])); - // Programs row - fetchItems(apiClient, { IncludeItemTypes: 'LiveTvProgram' }) - .then(result => setPrograms(result.Items)) - .catch(() => setPrograms([])); - // Channels row - fetchItems(apiClient, { IncludeItemTypes: 'TvChannel' }) - .then(result => setChannels(result.Items)) - .catch(() => setChannels([])); - // Photo Albums row - fetchItems(apiClient, { IncludeItemTypes: 'PhotoAlbum' }) - .then(result => setPhotoAlbums(result.Items)) - .catch(() => setPhotoAlbums([])); - // Photos row - fetchItems(apiClient, { IncludeItemTypes: 'Photo' }) - .then(result => setPhotos(result.Items)) - .catch(() => setPhotos([])); - // Audio Books row - fetchItems(apiClient, { IncludeItemTypes: 'AudioBook' }) - .then(result => setAudioBooks(result.Items)) - .catch(() => setAudioBooks([])); - // Books row - fetchItems(apiClient, { IncludeItemTypes: 'Book' }) - .then(result => setBooks(result.Items)) - .catch(() => setBooks([])); - // Collections row - fetchItems(apiClient, { IncludeItemTypes: 'BoxSet' }) - .then(result => setCollections(result.Items)) - .catch(() => setCollections([])); + fetchPromises.push( + // Videos row + fetchItems(apiClient, { + MediaTypes: 'Video', + ExcludeItemTypes: 'Movie,Episode,TvChannel' + }) + .then(result => setVideos(result.Items)) + .catch(() => setVideos([])), + // Programs row + fetchItems(apiClient, { IncludeItemTypes: 'LiveTvProgram' }) + .then(result => setPrograms(result.Items)) + .catch(() => setPrograms([])), + // Channels row + fetchItems(apiClient, { IncludeItemTypes: 'TvChannel' }) + .then(result => setChannels(result.Items)) + .catch(() => setChannels([])), + // Photo Albums row + fetchItems(apiClient, { IncludeItemTypes: 'PhotoAlbum' }) + .then(result => setPhotoAlbums(result.Items)) + .catch(() => setPhotoAlbums([])), + // Photos row + fetchItems(apiClient, { IncludeItemTypes: 'Photo' }) + .then(result => setPhotos(result.Items)) + .catch(() => setPhotos([])), + // Audio Books row + fetchItems(apiClient, { IncludeItemTypes: 'AudioBook' }) + .then(result => setAudioBooks(result.Items)) + .catch(() => setAudioBooks([])), + // Books row + fetchItems(apiClient, { IncludeItemTypes: 'Book' }) + .then(result => setBooks(result.Items)) + .catch(() => setBooks([])), + // Collections row + fetchItems(apiClient, { IncludeItemTypes: 'BoxSet' }) + .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]); + const allEmpty = [movies, shows, episodes, videos, programs, channels, playlists, artists, albums, songs, photoAlbums, photos, audioBooks, books, people, collections].every(arr => arr.length === 0); + return (
= ({ serverId = windo { 'hide': !query || collectionType === 'livetv' } )} > - - - - - - - - - - - - - - - - + {isLoading ? ( +
Loading...
// Replace this with your preferred progress indicator + ) : ( + <> + + + + + + + + + + + + + + + + + + {allEmpty && !isLoading && ( +
Sorry, nothing's here :/
+ )} + + )} +
); }; From 838d496aab3f4f630115b7647c08398fa6ba92be Mon Sep 17 00:00:00 2001 From: Nathan G Date: Sun, 1 Oct 2023 18:16:19 -0700 Subject: [PATCH 02/11] Main Iteration --- src/components/search/SearchResults.tsx | 4 ++-- src/components/search/searchfields.scss | 31 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx index bb0ca208473..ae58bc9465f 100644 --- a/src/components/search/SearchResults.tsx +++ b/src/components/search/SearchResults.tsx @@ -241,7 +241,7 @@ const SearchResults: FunctionComponent = ({ serverId = windo )} > {isLoading ? ( -
Loading...
// Replace this with your preferred progress indicator +
) : ( <> = ({ serverId = windo /> {allEmpty && !isLoading && ( -
Sorry, nothing's here :/
+
Sorry! No results found for "{query}"
)} )} diff --git a/src/components/search/searchfields.scss b/src/components/search/searchfields.scss index 08d8515c865..4580dfcb802 100644 --- a/src/components/search/searchfields.scss +++ b/src/components/search/searchfields.scss @@ -9,3 +9,34 @@ font-size: 2em; 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%); +} + +// New styles for loading circle +.loading-circle { + border: 5px solid #557ece; + border-radius: 50%; + border-top: 5px solid #1b1b1bd7; + width: 65px; + height: 65px; + animation: spin 1s linear infinite; + margin: auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +@keyframes spin { + 0% { transform: translate(-50%, -50%) rotate(0deg); } + 100% { transform: translate(-50%, -50%) rotate(360deg); } +} From e615621d3b2d903d6534f60271a75b0b67dc5dda Mon Sep 17 00:00:00 2001 From: Nathan G Date: Sun, 1 Oct 2023 19:20:31 -0700 Subject: [PATCH 03/11] Delete jellyfin-web.code-workspace --- jellyfin-web.code-workspace | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 jellyfin-web.code-workspace diff --git a/jellyfin-web.code-workspace b/jellyfin-web.code-workspace deleted file mode 100644 index 876a1499c09..00000000000 --- a/jellyfin-web.code-workspace +++ /dev/null @@ -1,8 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ], - "settings": {} -} \ No newline at end of file From 4c486286a0ce0be48095222db93e0fef2f703d4e Mon Sep 17 00:00:00 2001 From: Nathan G Date: Sun, 1 Oct 2023 19:52:54 -0700 Subject: [PATCH 04/11] Update CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index eccfd4cf279..d935489893f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -66,6 +66,7 @@ - [Fishbigger](https://github.com/fishbigger) - [sleepycatcoding](https://github.com/sleepycatcoding) - [TheMelmacian](https://github.com/TheMelmacian) + - [Nate G](https://github.com/GGProGaming) # Emby Contributors From 0b60343a3bae3ad536ff36493710fdc79640ae84 Mon Sep 17 00:00:00 2001 From: Nathan G Date: Mon, 2 Oct 2023 11:10:31 -0700 Subject: [PATCH 05/11] Move setIsLoading below if - Reduce calls --- src/components/search/SearchResults.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx index ae58bc9465f..b934ff4ceec 100644 --- a/src/components/search/SearchResults.tsx +++ b/src/components/search/SearchResults.tsx @@ -113,13 +113,14 @@ const SearchResults: FunctionComponent = ({ serverId = windo setBooks([]); setPeople([]); setCollections([]); - setIsLoading(true); if (!query) { setIsLoading(false); return; } + setIsLoading(true); + const apiClient = ServerConnections.getApiClient(serverId); const fetchPromises = []; From bf367a295c421875e85218133f6f7f81bf274721 Mon Sep 17 00:00:00 2001 From: Nathan G Date: Thu, 5 Oct 2023 18:38:00 +0000 Subject: [PATCH 06/11] Replace loading-circle handling with native --- src/components/search/SearchResults.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx index b934ff4ceec..04607ef92f4 100644 --- a/src/components/search/SearchResults.tsx +++ b/src/components/search/SearchResults.tsx @@ -6,6 +6,7 @@ import React, { FunctionComponent, useCallback, useEffect, useState } from 'reac import globalize from '../../scripts/globalize'; import ServerConnections from '../ServerConnections'; import SearchResultsRow from './SearchResultsRow'; +import Loading from '../loading/LoadingComponent'; type SearchResultsProps = { serverId?: string; @@ -242,7 +243,7 @@ const SearchResults: FunctionComponent = ({ serverId = windo )} > {isLoading ? ( -
+ ) : ( <> Date: Wed, 11 Oct 2023 07:34:27 -0700 Subject: [PATCH 07/11] Remove the unused loading circle - Move text to en-us.json --- src/components/search/SearchResults.tsx | 2 +- src/components/search/searchfields.scss | 20 -------------------- src/strings/en-us.json | 1 + 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx index 04607ef92f4..56f618ccccc 100644 --- a/src/components/search/SearchResults.tsx +++ b/src/components/search/SearchResults.tsx @@ -335,7 +335,7 @@ const SearchResults: FunctionComponent = ({ serverId = windo /> {allEmpty && !isLoading && ( -
Sorry! No results found for "{query}"
+
{globalize.translate('SearchResultsEmpty')}"{query}"
)} )} diff --git a/src/components/search/searchfields.scss b/src/components/search/searchfields.scss index 4580dfcb802..b93638bdf3f 100644 --- a/src/components/search/searchfields.scss +++ b/src/components/search/searchfields.scss @@ -20,23 +20,3 @@ left: 50%; transform: translate(-50%, -50%); } - -// New styles for loading circle -.loading-circle { - border: 5px solid #557ece; - border-radius: 50%; - border-top: 5px solid #1b1b1bd7; - width: 65px; - height: 65px; - animation: spin 1s linear infinite; - margin: auto; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -@keyframes spin { - 0% { transform: translate(-50%, -50%) rotate(0deg); } - 100% { transform: translate(-50%, -50%) rotate(360deg); } -} diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 46ac5869511..88b281ec849 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1427,6 +1427,7 @@ "SearchForMissingMetadata": "Search for missing metadata", "SearchForSubtitles": "Search for Subtitles", "SearchResults": "Search Results", + "SearchResultsEmpty": "Sorry! No results found for ", "Season": "Season", "SecondarySubtitles": "Secondary Subtitles", "SelectAdminUsername": "Please select a username for the admin account.", From 06ee15f40146b06b1ec2c5a03ea141c5df632ef5 Mon Sep 17 00:00:00 2001 From: Nathan G Date: Fri, 20 Oct 2023 08:46:44 -0700 Subject: [PATCH 08/11] Update src/strings/en-us.json Change SearchResultsEmpty to have a substitution Co-authored-by: Bill Thornton --- src/strings/en-us.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 88b281ec849..b6eee5bfe1c 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1427,7 +1427,7 @@ "SearchForMissingMetadata": "Search for missing metadata", "SearchForSubtitles": "Search for Subtitles", "SearchResults": "Search Results", - "SearchResultsEmpty": "Sorry! No results found for ", + "SearchResultsEmpty": "Sorry! No results found for \"{0}\"", "Season": "Season", "SecondarySubtitles": "Secondary Subtitles", "SelectAdminUsername": "Please select a username for the admin account.", From bc35011232b3cac37b04721860899f25d55d71de Mon Sep 17 00:00:00 2001 From: Nathan G Date: Fri, 20 Oct 2023 08:47:38 -0700 Subject: [PATCH 09/11] Update src/components/search/SearchResults.tsx Update SearchResults Co-authored-by: Bill Thornton --- src/components/search/SearchResults.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx index 2ee920a1d94..28656f4c8f7 100644 --- a/src/components/search/SearchResults.tsx +++ b/src/components/search/SearchResults.tsx @@ -335,7 +335,7 @@ const SearchResults: FunctionComponent = ({ serverId = windo /> {allEmpty && !isLoading && ( -
{globalize.translate('SearchResultsEmpty')}"{query}"
+
{globalize.translate('SearchResultsEmpty', query)
)} )} From 173f2c4f6f9a2e577e0cf1ca4912a227fd64bc45 Mon Sep 17 00:00:00 2001 From: Nathan G Date: Fri, 20 Oct 2023 11:15:38 -0700 Subject: [PATCH 10/11] Update src/components/search/SearchResults.tsx Oops! Co-authored-by: Bill Thornton --- src/components/search/SearchResults.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx index 28656f4c8f7..ae9a620391a 100644 --- a/src/components/search/SearchResults.tsx +++ b/src/components/search/SearchResults.tsx @@ -335,7 +335,7 @@ const SearchResults: FunctionComponent = ({ serverId = windo /> {allEmpty && !isLoading && ( -
{globalize.translate('SearchResultsEmpty', query)
+
{globalize.translate('SearchResultsEmpty', query)}
)} )} From 21157b8ab0e492b006b1c15f8e9a28a5daff5b01 Mon Sep 17 00:00:00 2001 From: Nathan G Date: Mon, 23 Oct 2023 22:28:33 -0700 Subject: [PATCH 11/11] Update src/components/search/SearchResults.tsx Co-authored-by: Bill Thornton --- src/components/search/SearchResults.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx index ae9a620391a..2f4befcc2f2 100644 --- a/src/components/search/SearchResults.tsx +++ b/src/components/search/SearchResults.tsx @@ -334,7 +334,7 @@ const SearchResults: FunctionComponent = ({ serverId = windo cardOptions={{ coverImage: true }} /> - {allEmpty && !isLoading && ( + {allEmpty && query && !isLoading && (
{globalize.translate('SearchResultsEmpty', query)}
)}