initial sections for book library view

This commit is contained in:
dkanada
2026-03-18 23:13:15 +09:00
parent 6099a5e5a2
commit 23cf060453
10 changed files with 148 additions and 3 deletions
@@ -83,6 +83,13 @@ const sortOptionsMapping: SortOptionsMapping = {
{ label: 'OptionReleaseDate', value: ItemSortBy.ProductionYear },
{ label: 'OptionDateAdded', value: ItemSortBy.DateCreated }
],
[LibraryTab.Books]: [
{ label: 'Name', value: ItemSortBy.SortName },
{ label: 'OptionReleaseDate', value: ItemSortBy.ProductionYear },
{ label: 'OptionDateAdded', value: ItemSortBy.DateCreated },
{ label: 'OptionDatePlayed', value: ItemSortBy.DatePlayed },
{ label: 'OptionRandom', value: ItemSortBy.Random }
],
[LibraryTab.Songs]: [
{ label: 'Name', value: ItemSortBy.SortName },
{ label: 'Album', value: ItemSortBy.Album },
@@ -135,6 +135,7 @@ const FilterButton: FC<FilterButtonProps> = ({
|| viewType === LibraryTab.Artists
|| viewType === LibraryTab.Songs
|| viewType === LibraryTab.Episodes
|| viewType === LibraryTab.Books
);
};
@@ -142,6 +143,7 @@ const FilterButton: FC<FilterButtonProps> = ({
return (
viewType === LibraryTab.Movies
|| viewType === LibraryTab.Series
|| viewType === LibraryTab.Books
);
};
@@ -8,7 +8,7 @@ import globalize from 'lib/globalize';
import { LibraryViewSettings } from 'types/library';
import { LibraryTab } from 'types/libraryTab';
const statusFiltersOptions = [
const defaultFiltersOptions = [
{ label: 'Played', value: ItemFilter.IsPlayed },
{ label: 'Unplayed', value: ItemFilter.IsUnplayed },
{ label: 'Favorite', value: ItemFilter.IsFavorite },
@@ -26,6 +26,14 @@ const FiltersStatus: FC<FiltersStatusProps> = ({
libraryViewSettings,
setLibraryViewSettings
}) => {
let statusFiltersOptions = defaultFiltersOptions;
if (viewType === LibraryTab.Books) {
statusFiltersOptions = defaultFiltersOptions.map((option) => (
option.value === ItemFilter.IsResumable ? { ...option, label: 'ContinueReading' } : option
));
}
const onFiltersStatusChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
event.preventDefault();
@@ -39,6 +39,32 @@ export const LibraryRoutes: LibraryRoute[] = [
}
]
},
{
path: '/books',
views: [
{
index: 0,
label: 'Books',
view: LibraryTab.Books,
isDefault: true
},
{
index: 1,
label: 'Suggestions',
view: LibraryTab.Suggestions
},
{
index: 2,
label: 'Genres',
view: LibraryTab.Genres
},
{
index: 3,
label: 'Favorites',
view: LibraryTab.Favorites
}
]
},
{
path: '/movies',
views: [
@@ -7,6 +7,7 @@ export const ASYNC_USER_ROUTES: AsyncRoute[] = [
{ path: 'livetv', type: AppType.Experimental },
{ path: 'movies', type: AppType.Experimental },
{ path: 'music', type: AppType.Experimental },
{ path: 'books', type: AppType.Experimental },
{ path: 'mypreferencesdisplay', page: 'user/display', type: AppType.Experimental },
{ path: 'mypreferencesmenu', page: 'user/settings' },
{ path: 'quickconnect', page: 'quickConnect' },
@@ -0,0 +1,60 @@
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
import React, { FC } from 'react';
import useCurrentTab from 'hooks/useCurrentTab';
import Page from 'components/Page';
import PageTabContent from '../../components/library/PageTabContent';
import { LibraryTab } from 'types/libraryTab';
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
import { LibraryTabContent, LibraryTabMapping } from 'types/libraryTabContent';
import { BookSuggestionsSectionsView } from 'types/sections';
const booksTabContent: LibraryTabContent = {
viewType: LibraryTab.Books,
collectionType: CollectionType.Books,
itemType: [BaseItemKind.Book]
};
const suggestionsTabContent: LibraryTabContent = {
viewType: LibraryTab.Suggestions,
collectionType: CollectionType.Books,
sectionsView: BookSuggestionsSectionsView
};
const genresTabContent: LibraryTabContent = {
viewType: LibraryTab.Genres,
collectionType: CollectionType.Books,
itemType: [BaseItemKind.Book]
};
const favoritesTabContent: LibraryTabContent = {
viewType: LibraryTab.Favorites,
collectionType: CollectionType.Books,
itemType: [BaseItemKind.Book]
};
const booksTabMapping: LibraryTabMapping = {
0: booksTabContent,
1: suggestionsTabContent,
2: genresTabContent,
3: favoritesTabContent
};
const Books: FC = () => {
const { libraryId, activeTab } = useCurrentTab();
const currentTab = booksTabMapping[activeTab];
return (
<Page
id='booksPage'
className='mainAnimatedPage libraryPage pageWithAbsoluteTabs withTabs'
>
<PageTabContent
key={`${currentTab.viewType} - ${libraryId}`}
currentTab={currentTab}
parentId={libraryId}
/>
</Page>
);
};
export default Books;
+6 -2
View File
@@ -401,6 +401,12 @@ class AppRouter {
}
if (context !== 'folders' && !itemHelper.isLocalItem(item)) {
const layoutMode = localStorage.getItem('layout') ?? LayoutMode.Experimental;
if (layoutMode === LayoutMode.Experimental && item.CollectionType == CollectionType.Books) {
return `#/books?topParentId=${item.Id}`;
}
if (item.CollectionType == CollectionType.Movies) {
url = `#/movies?topParentId=${item.Id}&collectionType=${item.CollectionType}`;
@@ -431,8 +437,6 @@ class AppRouter {
return url;
}
const layoutMode = localStorage.getItem('layout');
if (layoutMode === LayoutMode.Experimental && item.CollectionType == CollectionType.Homevideos) {
url = '#/homevideos?topParentId=' + item.Id;
+2
View File
@@ -193,6 +193,7 @@
"ConnectAnyway": "Connect Anyway",
"Console": "Console",
"ContinueWatching": "Continue watching",
"ContinueReading": "Continue reading",
"Continuing": "Continuing",
"Copied": "Copied",
"Copy": "Copy",
@@ -458,6 +459,7 @@
"HeaderKeepRecording": "Keep Recording",
"HeaderKeepSeries": "Keep Series",
"HeaderKodiMetadataHelp": "To enable or disable NFO metadata, edit a library and find the 'Metadata savers' section.",
"HeaderLatestBooks": "Recently Added Books",
"HeaderLatestEpisodes": "Recently Added Episodes",
"HeaderLatestMedia": "Recently Added Media",
"HeaderLatestMovies": "Recently Added Movies",
+9
View File
@@ -51,6 +51,8 @@ export enum SectionType {
LatestRecordings = 'LatestRecordings',
RecordingFolders = 'RecordingFolders',
ActiveRecordings = 'ActiveRecordings',
ContinueReading = 'ContinueReading',
LatestBooks = 'LatestBooks',
UpcomingRecordings = 'UpcomingRecordings'
}
@@ -63,6 +65,13 @@ export interface Section {
cardOptions: CardOptions;
}
export const BookSuggestionsSectionsView: SectionsView = {
suggestionSections: [
SectionType.LatestBooks,
SectionType.ContinueReading
]
};
export const MovieSuggestionsSectionsView: SectionsView = {
suggestionSections: [
SectionType.ContinueWatchingMovies,
+26
View File
@@ -38,6 +38,32 @@ export const getSuggestionSections = (): Section[] => {
showYear: true
}
},
{
name: 'HeaderContinueReading',
apiMethod: SectionApiMethod.ResumeItems,
itemTypes: 'Book',
type: SectionType.ContinueReading,
parametersOptions: {
includeItemTypes: [BaseItemKind.Book]
},
cardOptions: {
overlayPlayButton: true,
shape: CardShape.PortraitOverflow
}
},
{
name: 'HeaderLatestBooks',
apiMethod: SectionApiMethod.LatestMedia,
itemTypes: 'Book',
type: SectionType.LatestBooks,
parametersOptions: {
includeItemTypes: [BaseItemKind.Book]
},
cardOptions: {
overlayPlayButton: true,
shape: CardShape.PortraitOverflow
}
},
{
name: 'HeaderLatestMovies',
apiMethod: SectionApiMethod.LatestMedia,