mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2026-09-03 03:50:02 +03:00
initial sections for book library view
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user