mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2026-09-03 03:50:02 +03:00
Merge library toolbar into main AppBar
This commit is contained in:
@@ -12,6 +12,9 @@ import { useApi } from 'hooks/useApi';
|
||||
|
||||
import AppToolbar from './components/AppToolbar';
|
||||
import AppDrawer, { isDrawerPath } from './components/drawers/AppDrawer';
|
||||
import LibraryToolbar from './features/libraries/components/LibraryToolbar';
|
||||
import { LibraryProvider } from './features/libraries/hooks/useLibrary';
|
||||
import { isLibraryPath } from './features/libraries/utils/path';
|
||||
|
||||
import './AppOverrides.scss';
|
||||
|
||||
@@ -29,7 +32,7 @@ export const Component = () => {
|
||||
}, [ isDrawerActive, setIsDrawerActive ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LibraryProvider>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
@@ -45,6 +48,7 @@ export const Component = () => {
|
||||
isDrawerOpen={isDrawerOpen}
|
||||
onDrawerButtonClick={onToggleDrawer}
|
||||
/>
|
||||
{isLibraryPath(location.pathname) && <LibraryToolbar />}
|
||||
</OffsetAppBar>
|
||||
|
||||
{
|
||||
@@ -73,6 +77,6 @@ export const Component = () => {
|
||||
</Box>
|
||||
<ThemeCss />
|
||||
<CustomCss />
|
||||
</>
|
||||
</LibraryProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -54,10 +54,7 @@ const GuideView: FC = () => {
|
||||
width: 'auto',
|
||||
paddingTop: '0',
|
||||
paddingBottom: '0 !important',
|
||||
top: {
|
||||
xs: '6.9em !important',
|
||||
lg: '4em !important'
|
||||
}
|
||||
top: '0 !important'
|
||||
}}
|
||||
/>;
|
||||
};
|
||||
|
||||
@@ -1,109 +1,46 @@
|
||||
import type { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
|
||||
import Box from '@mui/material/Box';
|
||||
import ButtonGroup from '@mui/material/ButtonGroup';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import type { Theme } from '@mui/material/styles';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import classNames from 'classnames';
|
||||
import React, { type FC, useCallback } from 'react';
|
||||
import React, { type FC, SetStateAction, useCallback } from 'react';
|
||||
|
||||
import { useLibrary } from 'apps/experimental/features/libraries/hooks/useLibrary';
|
||||
import { getDefaultLibraryViewSettings } from 'apps/experimental/features/libraries/utils/settings';
|
||||
import Cards from 'components/cardbuilder/Card/Cards';
|
||||
import { CardShape } from 'components/cardbuilder/utils/shape';
|
||||
import OffsetAppBar from 'components/OffsetAppBar';
|
||||
import { ItemAction } from 'constants/itemAction';
|
||||
import { useApi } from 'hooks/useApi';
|
||||
import { useLocalStorage } from 'hooks/useLocalStorage';
|
||||
import { useGetItemsViewByType } from 'hooks/useFetchItems';
|
||||
import { getDefaultLibraryViewSettings, getSettingsKey } from 'utils/items';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import ItemsContainer from 'elements/emby-itemscontainer/ItemsContainer';
|
||||
import NoItemsMessage from 'components/common/NoItemsMessage';
|
||||
import Lists from 'components/listview/List/Lists';
|
||||
import Cards from 'components/cardbuilder/Card/Cards';
|
||||
import { useItem } from 'hooks/useItem';
|
||||
import { useUserSettings } from 'hooks/useUserSettings';
|
||||
import globalize from 'lib/globalize';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import { type LibraryViewSettings, type ParentId, ViewMode } from 'types/library';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import { ItemAction } from 'constants/itemAction';
|
||||
import ItemsContainer from 'elements/emby-itemscontainer/ItemsContainer';
|
||||
import { useApi } from 'hooks/useApi';
|
||||
import type { CardOptions } from 'types/cardOptions';
|
||||
import { type LibraryViewSettings, ViewMode } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import type { ListOptions } from 'types/listOptions';
|
||||
|
||||
import AlphabetPicker from './AlphabetPicker';
|
||||
import FilterButton from './filter/FilterButton';
|
||||
import NewCollectionButton from './NewCollectionButton';
|
||||
import NewPlaylistButton from './NewPlaylistButton';
|
||||
import Pagination from './Pagination';
|
||||
import PlayAllButton from './PlayAllButton';
|
||||
import QueueButton from './QueueButton';
|
||||
import ShuffleButton from './ShuffleButton';
|
||||
import SortButton from './SortButton';
|
||||
import LibraryViewMenu from './LibraryViewMenu';
|
||||
import ViewSettingsButton from './ViewSettingsButton';
|
||||
|
||||
interface ItemsViewProps {
|
||||
viewType: LibraryTab;
|
||||
parentId: ParentId;
|
||||
itemType: BaseItemKind[];
|
||||
collectionType?: CollectionType;
|
||||
isPaginationEnabled?: boolean;
|
||||
isBtnPlayAllEnabled?: boolean;
|
||||
isBtnQueueEnabled?: boolean;
|
||||
isBtnShuffleEnabled?: boolean;
|
||||
isBtnSortEnabled?: boolean;
|
||||
isBtnFilterEnabled?: boolean;
|
||||
isBtnNewCollectionEnabled?: boolean;
|
||||
isBtnNewPlaylistEnabled?: boolean;
|
||||
isBtnGridListEnabled?: boolean;
|
||||
isAlphabetPickerEnabled?: boolean;
|
||||
noItemsMessage: string;
|
||||
}
|
||||
|
||||
const ItemsView: FC<ItemsViewProps> = ({
|
||||
viewType,
|
||||
parentId,
|
||||
collectionType,
|
||||
isPaginationEnabled = true,
|
||||
isBtnPlayAllEnabled = false,
|
||||
isBtnQueueEnabled = false,
|
||||
isBtnShuffleEnabled = false,
|
||||
isBtnSortEnabled = true,
|
||||
isBtnFilterEnabled = true,
|
||||
isBtnNewCollectionEnabled = false,
|
||||
isBtnNewPlaylistEnabled = false,
|
||||
isBtnGridListEnabled = true,
|
||||
isAlphabetPickerEnabled = true,
|
||||
itemType,
|
||||
noItemsMessage
|
||||
}) => {
|
||||
const [libraryViewSettings, setLibraryViewSettings] =
|
||||
useLocalStorage<LibraryViewSettings>(
|
||||
getSettingsKey(viewType, parentId),
|
||||
getDefaultLibraryViewSettings(viewType)
|
||||
);
|
||||
const isSmallScreen = useMediaQuery((t: Theme) => t.breakpoints.up('sm'));
|
||||
const ItemsView: FC = () => {
|
||||
const {
|
||||
id: parentId,
|
||||
collectionType,
|
||||
content,
|
||||
itemsResult,
|
||||
viewSettings,
|
||||
setViewSettings
|
||||
} = useLibrary();
|
||||
const viewType = content?.viewType ?? LibraryTab.Movies;
|
||||
const libraryViewSettings = viewSettings ?? getDefaultLibraryViewSettings(viewType);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const setLibraryViewSettings = setViewSettings ?? ((action: SetStateAction<LibraryViewSettings>) => { /* no-op */ });
|
||||
const { isAlphabetPickerEnabled, noItemsMessage } = content ?? {};
|
||||
|
||||
const { __legacyApiClient__ } = useApi();
|
||||
const {
|
||||
isPending,
|
||||
data: itemsResult,
|
||||
isPlaceholderData,
|
||||
refetch
|
||||
} = useGetItemsViewByType(
|
||||
viewType,
|
||||
parentId,
|
||||
itemType,
|
||||
libraryViewSettings
|
||||
);
|
||||
const { data: item } = useItem(parentId || undefined);
|
||||
|
||||
const getListOptions = useCallback(() => {
|
||||
const listOptions: ListOptions = {
|
||||
items: itemsResult?.Items ?? [],
|
||||
items: itemsResult?.data?.Items ?? [],
|
||||
context: collectionType
|
||||
};
|
||||
|
||||
@@ -121,7 +58,7 @@ const ItemsView: FC<ItemsViewProps> = ({
|
||||
}
|
||||
|
||||
return listOptions;
|
||||
}, [itemsResult?.Items, collectionType, viewType, libraryViewSettings.SortBy]);
|
||||
}, [itemsResult?.data?.Items, collectionType, viewType, libraryViewSettings.SortBy]);
|
||||
|
||||
const getCardOptions = useCallback(() => {
|
||||
let shape;
|
||||
@@ -145,16 +82,16 @@ const ItemsView: FC<ItemsViewProps> = ({
|
||||
}
|
||||
|
||||
const cardOptions: CardOptions = {
|
||||
shape: shape,
|
||||
shape,
|
||||
showTitle: libraryViewSettings.ShowTitle,
|
||||
showYear: libraryViewSettings.ShowYear,
|
||||
cardLayout: libraryViewSettings.CardLayout,
|
||||
centerText: true,
|
||||
context: collectionType,
|
||||
coverImage: true,
|
||||
preferThumb: preferThumb,
|
||||
preferDisc: preferDisc,
|
||||
preferLogo: preferLogo,
|
||||
preferThumb,
|
||||
preferDisc,
|
||||
preferLogo,
|
||||
overlayText: !libraryViewSettings.ShowTitle,
|
||||
imageType: libraryViewSettings.ImageType,
|
||||
queryKey: ['ItemsViewByType'],
|
||||
@@ -191,6 +128,7 @@ const ItemsView: FC<ItemsViewProps> = ({
|
||||
|
||||
return cardOptions;
|
||||
}, [
|
||||
__legacyApiClient__,
|
||||
libraryViewSettings.ShowTitle,
|
||||
libraryViewSettings.ImageType,
|
||||
libraryViewSettings.ShowYear,
|
||||
@@ -200,56 +138,34 @@ const ItemsView: FC<ItemsViewProps> = ({
|
||||
]);
|
||||
|
||||
const getItems = useCallback(() => {
|
||||
if (!itemsResult?.Items?.length) {
|
||||
return <NoItemsMessage message={noItemsMessage} />;
|
||||
if (!itemsResult?.data?.Items?.length) {
|
||||
return <NoItemsMessage message={noItemsMessage ?? 'MessageNoItemsAvailable'} />;
|
||||
}
|
||||
|
||||
if (libraryViewSettings.ViewMode === ViewMode.ListView) {
|
||||
return (
|
||||
<Lists
|
||||
items={itemsResult?.Items ?? []}
|
||||
items={itemsResult?.data?.Items ?? []}
|
||||
listOptions={getListOptions()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Cards
|
||||
items={itemsResult?.Items ?? []}
|
||||
items={itemsResult?.data?.Items ?? []}
|
||||
cardOptions={getCardOptions()}
|
||||
/>
|
||||
);
|
||||
}, [
|
||||
libraryViewSettings.ViewMode,
|
||||
itemsResult?.Items,
|
||||
itemsResult?.data?.Items,
|
||||
getListOptions,
|
||||
getCardOptions,
|
||||
noItemsMessage
|
||||
]);
|
||||
|
||||
const totalRecordCount = itemsResult?.TotalRecordCount ?? 0;
|
||||
const items = itemsResult?.Items ?? [];
|
||||
const hasFilters = Object.values(libraryViewSettings.Filters ?? {}).some(
|
||||
(filter) => !!filter
|
||||
);
|
||||
const hasSortName = libraryViewSettings.SortBy !== ItemSortBy.Random;
|
||||
|
||||
// Pagination
|
||||
const startIndex = libraryViewSettings.StartIndex ?? 0;
|
||||
const { libraryPageSize: paginationLimit } = useUserSettings();
|
||||
const paginationStart = totalRecordCount ? startIndex + 1 : 0;
|
||||
const paginationEnd = paginationLimit ?
|
||||
Math.min(startIndex + paginationLimit, totalRecordCount) :
|
||||
totalRecordCount;
|
||||
/** True if the data is larger than the page limit */
|
||||
const isPaginationRequired = paginationLimit > 0 && paginationLimit < totalRecordCount;
|
||||
|
||||
let itemCountDisplay = '\u2219'; // Bullet "operator" character as a loading indicator
|
||||
if (!isPending) {
|
||||
itemCountDisplay = isPaginationRequired ?
|
||||
globalize.translate('ListPaging', paginationStart, paginationEnd, totalRecordCount) :
|
||||
totalRecordCount;
|
||||
}
|
||||
|
||||
const itemsContainerClass = classNames(
|
||||
'padded-left padded-right',
|
||||
libraryViewSettings.ViewMode === ViewMode.ListView ?
|
||||
@@ -259,156 +175,6 @@ const ItemsView: FC<ItemsViewProps> = ({
|
||||
|
||||
return (
|
||||
<Box className='padded-bottom-page'>
|
||||
<OffsetAppBar
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
sx={theme => ({
|
||||
top: 0,
|
||||
paddingTop: 6,
|
||||
// This should render under the main AppBar and AlphabetPicker
|
||||
zIndex: theme.zIndex.appBar - 2
|
||||
})}
|
||||
>
|
||||
<Toolbar
|
||||
className='padded-left padded-right'
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<LibraryViewMenu />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexGrow: {
|
||||
xs: 1,
|
||||
sm: 0
|
||||
},
|
||||
justifyContent: 'flex-end',
|
||||
marginLeft: 1
|
||||
}}
|
||||
>
|
||||
{!isPending && (
|
||||
<>
|
||||
<ButtonGroup
|
||||
variant='contained'
|
||||
>
|
||||
{isBtnPlayAllEnabled && totalRecordCount > 0 && (
|
||||
<PlayAllButton
|
||||
item={item}
|
||||
items={items}
|
||||
viewType={viewType}
|
||||
collectionType={collectionType}
|
||||
hasFilters={hasFilters}
|
||||
isTextVisible={isSmallScreen}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isBtnShuffleEnabled && totalRecordCount > 1 && (
|
||||
<ShuffleButton
|
||||
item={item}
|
||||
items={items}
|
||||
viewType={viewType}
|
||||
collectionType={collectionType}
|
||||
hasFilters={hasFilters}
|
||||
isTextVisible={isSmallScreen && !isBtnPlayAllEnabled}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isBtnQueueEnabled && item && playbackManager.canQueue(item) && (
|
||||
<QueueButton
|
||||
item={item}
|
||||
items={items}
|
||||
hasFilters={hasFilters}
|
||||
isTextVisible={isSmallScreen && !isBtnPlayAllEnabled && !isBtnShuffleEnabled}
|
||||
/>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
|
||||
{isBtnNewCollectionEnabled && <NewCollectionButton isTextVisible={isSmallScreen} />}
|
||||
{isBtnNewPlaylistEnabled && <NewPlaylistButton isTextVisible={isSmallScreen} />}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Stack
|
||||
direction='row'
|
||||
spacing={1}
|
||||
sx={{
|
||||
justifyContent: {
|
||||
xs: 'auto',
|
||||
sm: 'end'
|
||||
},
|
||||
flexBasis: {
|
||||
xs: '100%',
|
||||
sm: 'auto'
|
||||
},
|
||||
flexGrow: 1,
|
||||
marginTop: 0.5,
|
||||
marginBottom: 0.5
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexGrow: {
|
||||
xs: 1,
|
||||
sm: 0
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Chip label={itemCountDisplay} />
|
||||
</Box>
|
||||
|
||||
<ButtonGroup
|
||||
color='inherit'
|
||||
variant='text'
|
||||
>
|
||||
{isBtnFilterEnabled && (
|
||||
<FilterButton
|
||||
parentId={parentId}
|
||||
itemType={itemType}
|
||||
viewType={viewType}
|
||||
hasFilters={hasFilters}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isBtnSortEnabled && (
|
||||
<SortButton
|
||||
viewType={viewType}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isBtnGridListEnabled && (
|
||||
<ViewSettingsButton
|
||||
viewType={viewType}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
|
||||
{isPaginationEnabled && (
|
||||
<Pagination
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
index={startIndex}
|
||||
pageSize={paginationLimit}
|
||||
total={totalRecordCount}
|
||||
disabled={isPending || !isPaginationRequired || isPlaceholderData}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Toolbar>
|
||||
</OffsetAppBar>
|
||||
|
||||
{isAlphabetPickerEnabled && hasSortName && (
|
||||
<AlphabetPicker
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
@@ -416,13 +182,13 @@ const ItemsView: FC<ItemsViewProps> = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{isPending ? (
|
||||
{(!itemsResult || itemsResult.isPending) ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<ItemsContainer
|
||||
className={itemsContainerClass}
|
||||
parentId={parentId}
|
||||
reloadItems={refetch}
|
||||
reloadItems={itemsResult?.refetch}
|
||||
queryKey={['ItemsViewByType']}
|
||||
>
|
||||
{getItems()}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import Box from '@mui/material/Box/Box';
|
||||
import React, { type FC } from 'react';
|
||||
|
||||
import type { ParentId } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import type { LibraryTabContent } from 'types/libraryTabContent';
|
||||
|
||||
import GenresView from './GenresView';
|
||||
import GuideView from './GuideView';
|
||||
import ItemsView from './ItemsView';
|
||||
import ProgramsSectionView from './ProgramsSectionView';
|
||||
import SuggestionsSectionView from './SuggestionsSectionView';
|
||||
import UpcomingView from './UpcomingView';
|
||||
import GenresView from './GenresView';
|
||||
import ItemsView from './ItemsView';
|
||||
import GuideView from './GuideView';
|
||||
import ProgramsSectionView from './ProgramsSectionView';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import type { ParentId } from 'types/library';
|
||||
import type { LibraryTabContent } from 'types/libraryTabContent';
|
||||
import LibraryViewMenu from './LibraryViewMenu';
|
||||
|
||||
interface PageTabContentProps {
|
||||
parentId: ParentId;
|
||||
@@ -19,108 +19,54 @@ interface PageTabContentProps {
|
||||
const PageTabContent: FC<PageTabContentProps> = ({ parentId, currentTab }) => {
|
||||
if (currentTab.viewType === LibraryTab.Suggestions) {
|
||||
return (
|
||||
<>
|
||||
<Box className='padded-top padded-left padded-right padded-bottom'>
|
||||
<LibraryViewMenu />
|
||||
</Box>
|
||||
|
||||
<SuggestionsSectionView
|
||||
parentId={parentId}
|
||||
sectionType={
|
||||
currentTab.sectionsView?.suggestionSections ?? []
|
||||
}
|
||||
isMovieRecommendationEnabled={
|
||||
currentTab.sectionsView?.isMovieRecommendations
|
||||
}
|
||||
/>
|
||||
</>
|
||||
<SuggestionsSectionView
|
||||
parentId={parentId}
|
||||
sectionType={
|
||||
currentTab.sectionsView?.suggestionSections ?? []
|
||||
}
|
||||
isMovieRecommendationEnabled={
|
||||
currentTab.sectionsView?.isMovieRecommendations
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (currentTab.viewType === LibraryTab.Programs || currentTab.viewType === LibraryTab.Recordings || currentTab.viewType === LibraryTab.Schedule) {
|
||||
return (
|
||||
<>
|
||||
<Box className='padded-top padded-left padded-right padded-bottom'>
|
||||
<LibraryViewMenu />
|
||||
</Box>
|
||||
|
||||
<ProgramsSectionView
|
||||
parentId={parentId}
|
||||
sectionType={
|
||||
currentTab.sectionsView?.programSections ?? []
|
||||
}
|
||||
isUpcomingRecordingsEnabled={currentTab.sectionsView?.isLiveTvUpcomingRecordings}
|
||||
/>
|
||||
</>
|
||||
<ProgramsSectionView
|
||||
parentId={parentId}
|
||||
sectionType={
|
||||
currentTab.sectionsView?.programSections ?? []
|
||||
}
|
||||
isUpcomingRecordingsEnabled={currentTab.sectionsView?.isLiveTvUpcomingRecordings}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (currentTab.viewType === LibraryTab.Upcoming) {
|
||||
return (
|
||||
<>
|
||||
<Box className='padded-top padded-left padded-right padded-bottom'>
|
||||
<LibraryViewMenu />
|
||||
</Box>
|
||||
|
||||
<UpcomingView parentId={parentId} />
|
||||
</>
|
||||
<UpcomingView parentId={parentId} />
|
||||
);
|
||||
}
|
||||
|
||||
if (currentTab.viewType === LibraryTab.Genres) {
|
||||
return (
|
||||
<>
|
||||
<Box className='padded-top padded-left padded-right padded-bottom'>
|
||||
<LibraryViewMenu />
|
||||
</Box>
|
||||
|
||||
<GenresView
|
||||
parentId={parentId}
|
||||
collectionType={currentTab.collectionType ?? undefined}
|
||||
itemType={currentTab.itemType || []}
|
||||
/>
|
||||
</>
|
||||
<GenresView
|
||||
parentId={parentId}
|
||||
collectionType={currentTab.collectionType ?? undefined}
|
||||
itemType={currentTab.itemType || []}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (currentTab.viewType === LibraryTab.Guide) {
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
className='padded-top padded-left padded-right padded-bottom'
|
||||
sx={{
|
||||
position: 'relative',
|
||||
zIndex: 2
|
||||
}}
|
||||
>
|
||||
<LibraryViewMenu />
|
||||
</Box>
|
||||
|
||||
<GuideView />
|
||||
</>
|
||||
<GuideView />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ItemsView
|
||||
viewType={currentTab.viewType}
|
||||
parentId={parentId}
|
||||
collectionType={currentTab.collectionType ?? undefined}
|
||||
isPaginationEnabled={currentTab.isPaginationEnabled}
|
||||
isBtnPlayAllEnabled={currentTab.isBtnPlayAllEnabled}
|
||||
isBtnQueueEnabled={currentTab.isBtnQueueEnabled}
|
||||
isBtnShuffleEnabled={currentTab.isBtnShuffleEnabled}
|
||||
isBtnNewCollectionEnabled={currentTab.isBtnNewCollectionEnabled}
|
||||
isBtnNewPlaylistEnabled={currentTab.isBtnNewPlaylistEnabled}
|
||||
isBtnFilterEnabled={currentTab.isBtnFilterEnabled}
|
||||
isBtnGridListEnabled={currentTab.isBtnGridListEnabled}
|
||||
isBtnSortEnabled={currentTab.isBtnSortEnabled}
|
||||
isAlphabetPickerEnabled={currentTab.isAlphabetPickerEnabled}
|
||||
itemType={currentTab.itemType || []}
|
||||
noItemsMessage={
|
||||
currentTab.noItemsMessage || 'MessageNoItemsAvailable'
|
||||
}
|
||||
/>
|
||||
<ItemsView />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
import Box from '@mui/material/Box';
|
||||
import ButtonGroup from '@mui/material/ButtonGroup';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import React, { type SetStateAction, useMemo, type FC } from 'react';
|
||||
|
||||
import FilterButton from 'apps/experimental/components/library/filter/FilterButton';
|
||||
import LibraryViewMenu from 'apps/experimental/components/library/LibraryViewMenu';
|
||||
import NewCollectionButton from 'apps/experimental/components/library/NewCollectionButton';
|
||||
import NewPlaylistButton from 'apps/experimental/components/library/NewPlaylistButton';
|
||||
import Pagination from 'apps/experimental/components/library/Pagination';
|
||||
import PlayAllButton from 'apps/experimental/components/library/PlayAllButton';
|
||||
import QueueButton from 'apps/experimental/components/library/QueueButton';
|
||||
import ShuffleButton from 'apps/experimental/components/library/ShuffleButton';
|
||||
import SortButton from 'apps/experimental/components/library/SortButton';
|
||||
import ViewSettingsButton from 'apps/experimental/components/library/ViewSettingsButton';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import { useItem } from 'hooks/useItem';
|
||||
import { useUserSettings } from 'hooks/useUserSettings';
|
||||
import globalize from 'lib/globalize';
|
||||
import type { LibraryViewSettings } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
import { useLibrary } from '../hooks/useLibrary';
|
||||
import { getDefaultLibraryViewSettings } from '../utils/settings';
|
||||
|
||||
/** Views that only show the menu, not the toolbar buttons */
|
||||
const MENU_ONLY_VIEWS = [
|
||||
LibraryTab.Genres,
|
||||
LibraryTab.Guide,
|
||||
LibraryTab.Suggestions,
|
||||
LibraryTab.Programs,
|
||||
LibraryTab.Recordings,
|
||||
LibraryTab.Schedule,
|
||||
LibraryTab.Upcoming
|
||||
];
|
||||
|
||||
const LibraryToolbar: FC = () => {
|
||||
const {
|
||||
id: parentId,
|
||||
collectionType,
|
||||
content,
|
||||
isLibraryPath,
|
||||
itemsResult,
|
||||
viewSettings,
|
||||
setViewSettings
|
||||
} = useLibrary();
|
||||
const viewType = content?.viewType ?? LibraryTab.Movies;
|
||||
const libraryViewSettings = viewSettings ?? getDefaultLibraryViewSettings(viewType);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const setLibraryViewSettings = setViewSettings ?? ((action: SetStateAction<LibraryViewSettings>) => { /* no-op */ });
|
||||
const { itemType, isPaginationEnabled, isBtnPlayAllEnabled, isBtnQueueEnabled, isBtnShuffleEnabled, isBtnSortEnabled, isBtnFilterEnabled, isBtnNewCollectionEnabled, isBtnNewPlaylistEnabled, isBtnGridListEnabled } = content ?? {};
|
||||
|
||||
const isSmallScreen = useMediaQuery(t => t.breakpoints.up('sm'));
|
||||
|
||||
const { data: item } = useItem(parentId || undefined);
|
||||
|
||||
const isPending = itemsResult?.isPending ?? true;
|
||||
const totalRecordCount = itemsResult?.data?.TotalRecordCount ?? 0;
|
||||
const items = itemsResult?.data?.Items ?? [];
|
||||
const hasFilters = Object.values(viewSettings?.Filters ?? {}).some(
|
||||
(filter) => !!filter
|
||||
);
|
||||
|
||||
// Pagination
|
||||
const startIndex = viewSettings?.StartIndex ?? 0;
|
||||
const { libraryPageSize: paginationLimit } = useUserSettings();
|
||||
const paginationStart = totalRecordCount ? startIndex + 1 : 0;
|
||||
const paginationEnd = paginationLimit ?
|
||||
Math.min(startIndex + paginationLimit, totalRecordCount) :
|
||||
totalRecordCount;
|
||||
/** True if the data is larger than the page limit */
|
||||
const isPaginationRequired = paginationLimit > 0 && paginationLimit < totalRecordCount;
|
||||
|
||||
const itemCountDisplay = useMemo(() => {
|
||||
if (isPending) return '\u2219'; // Bullet "operator" character as a loading indicator
|
||||
|
||||
return isPaginationRequired ?
|
||||
globalize.translate('ListPaging', paginationStart, paginationEnd, totalRecordCount) :
|
||||
totalRecordCount;
|
||||
}, [isPending, isPaginationRequired, paginationStart, paginationEnd, totalRecordCount]);
|
||||
|
||||
if (!isLibraryPath) return null;
|
||||
|
||||
return (
|
||||
<Toolbar
|
||||
className='padded-left padded-right'
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<LibraryViewMenu />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexGrow: {
|
||||
xs: 1,
|
||||
sm: 0
|
||||
},
|
||||
justifyContent: 'flex-end',
|
||||
marginLeft: 1
|
||||
}}
|
||||
>
|
||||
{!isPending && (
|
||||
<>
|
||||
<ButtonGroup
|
||||
variant='contained'
|
||||
>
|
||||
{isBtnPlayAllEnabled && totalRecordCount > 0 && (
|
||||
<PlayAllButton
|
||||
item={item}
|
||||
items={items}
|
||||
viewType={viewType}
|
||||
collectionType={collectionType}
|
||||
hasFilters={hasFilters}
|
||||
isTextVisible={isSmallScreen}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isBtnShuffleEnabled && totalRecordCount > 1 && (
|
||||
<ShuffleButton
|
||||
item={item}
|
||||
items={items}
|
||||
viewType={viewType}
|
||||
collectionType={collectionType}
|
||||
hasFilters={hasFilters}
|
||||
isTextVisible={isSmallScreen && !isBtnPlayAllEnabled}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isBtnQueueEnabled && item && playbackManager.canQueue(item) && (
|
||||
<QueueButton
|
||||
item={item}
|
||||
items={items}
|
||||
hasFilters={hasFilters}
|
||||
isTextVisible={isSmallScreen && !isBtnPlayAllEnabled && !isBtnShuffleEnabled}
|
||||
/>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
|
||||
{isBtnNewCollectionEnabled && <NewCollectionButton isTextVisible={isSmallScreen} />}
|
||||
{isBtnNewPlaylistEnabled && <NewPlaylistButton isTextVisible={isSmallScreen} />}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{!MENU_ONLY_VIEWS.includes(viewType) && (
|
||||
<Stack
|
||||
direction='row'
|
||||
spacing={1}
|
||||
sx={{
|
||||
justifyContent: {
|
||||
xs: 'auto',
|
||||
sm: 'end'
|
||||
},
|
||||
flexBasis: {
|
||||
xs: '100%',
|
||||
sm: 'auto'
|
||||
},
|
||||
flexGrow: 1,
|
||||
marginTop: 0.5,
|
||||
marginBottom: 0.5
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexGrow: {
|
||||
xs: 1,
|
||||
sm: 0
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Chip label={itemCountDisplay} />
|
||||
</Box>
|
||||
|
||||
<ButtonGroup
|
||||
color='inherit'
|
||||
variant='text'
|
||||
>
|
||||
{isBtnFilterEnabled && (
|
||||
<FilterButton
|
||||
parentId={parentId}
|
||||
itemType={itemType ?? []}
|
||||
viewType={viewType}
|
||||
hasFilters={hasFilters}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isBtnSortEnabled && (
|
||||
<SortButton
|
||||
viewType={viewType}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isBtnGridListEnabled && (
|
||||
<ViewSettingsButton
|
||||
viewType={viewType}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
|
||||
{isPaginationEnabled && (
|
||||
<Pagination
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
index={startIndex}
|
||||
pageSize={paginationLimit}
|
||||
total={totalRecordCount}
|
||||
disabled={isPending || !isPaginationRequired || itemsResult?.isPlaceholderData}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Toolbar>
|
||||
);
|
||||
};
|
||||
|
||||
export default LibraryToolbar;
|
||||
@@ -1,3 +1,5 @@
|
||||
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
import { LibraryRoute } from '../types/LibraryRoute';
|
||||
@@ -5,6 +7,7 @@ import { LibraryRoute } from '../types/LibraryRoute';
|
||||
export const LibraryRoutes: LibraryRoute[] = [
|
||||
{
|
||||
path: '/livetv',
|
||||
type: CollectionType.Livetv,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
@@ -41,6 +44,7 @@ export const LibraryRoutes: LibraryRoute[] = [
|
||||
},
|
||||
{
|
||||
path: '/books',
|
||||
type: CollectionType.Books,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
@@ -82,6 +86,7 @@ export const LibraryRoutes: LibraryRoute[] = [
|
||||
},
|
||||
{
|
||||
path: '/boxsets',
|
||||
type: CollectionType.Boxsets,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
@@ -103,6 +108,7 @@ export const LibraryRoutes: LibraryRoute[] = [
|
||||
},
|
||||
{
|
||||
path: '/movies',
|
||||
type: CollectionType.Movies,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
@@ -144,6 +150,7 @@ export const LibraryRoutes: LibraryRoute[] = [
|
||||
},
|
||||
{
|
||||
path: '/music',
|
||||
type: CollectionType.Music,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
@@ -190,6 +197,7 @@ export const LibraryRoutes: LibraryRoute[] = [
|
||||
},
|
||||
{
|
||||
path: '/tv',
|
||||
type: CollectionType.Tvshows,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
@@ -236,6 +244,7 @@ export const LibraryRoutes: LibraryRoute[] = [
|
||||
},
|
||||
{
|
||||
path: '/homevideos',
|
||||
type: CollectionType.Homevideos,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
@@ -262,6 +271,7 @@ export const LibraryRoutes: LibraryRoute[] = [
|
||||
},
|
||||
{
|
||||
path: '/musicvideos',
|
||||
type: CollectionType.Musicvideos,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
@@ -288,6 +298,7 @@ export const LibraryRoutes: LibraryRoute[] = [
|
||||
},
|
||||
{
|
||||
path: '/playlists',
|
||||
type: CollectionType.Playlists,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
@@ -304,6 +315,7 @@ export const LibraryRoutes: LibraryRoute[] = [
|
||||
},
|
||||
{
|
||||
path: '/mixed',
|
||||
type: CollectionType.Unknown,
|
||||
views: [
|
||||
{
|
||||
index: 0,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { LibraryTabContent } from 'types/libraryTabContent';
|
||||
|
||||
const DEFAULT_VIEW_CONTENT: Partial<LibraryTabContent> = {
|
||||
isPaginationEnabled: true,
|
||||
isBtnPlayAllEnabled: false,
|
||||
isBtnQueueEnabled: false,
|
||||
isBtnShuffleEnabled: false,
|
||||
isBtnSortEnabled: true,
|
||||
isBtnFilterEnabled: true,
|
||||
isBtnNewCollectionEnabled: false,
|
||||
isBtnNewPlaylistEnabled: false,
|
||||
isBtnGridListEnabled: true,
|
||||
isAlphabetPickerEnabled: true
|
||||
};
|
||||
|
||||
export default DEFAULT_VIEW_CONTENT;
|
||||
@@ -0,0 +1,79 @@
|
||||
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||
import { UseQueryResult } from '@tanstack/react-query';
|
||||
import React, { type FC, type PropsWithChildren, createContext, useContext, useMemo } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import useCurrentTab from 'hooks/useCurrentTab';
|
||||
import { useGetItemsViewByType } from 'hooks/useFetchItems';
|
||||
import { useLocalStorage } from 'hooks/useLocalStorage';
|
||||
import { ItemDtoQueryResult } from 'types/base/models/item-dto-query-result';
|
||||
import { LibraryViewSettings } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import { LibraryTabContent } from 'types/libraryTabContent';
|
||||
|
||||
import { LibraryRoutes } from '../constants/libraryRoutes';
|
||||
import { isLibraryPath } from '../utils/path';
|
||||
import { getDefaultLibraryViewSettings, getSettingsKey } from '../utils/settings';
|
||||
import { getViewContent } from '../utils/viewContent';
|
||||
|
||||
interface LibraryState {
|
||||
collectionType?: CollectionType;
|
||||
content?: LibraryTabContent;
|
||||
isLibraryPath: boolean;
|
||||
id?: string;
|
||||
itemsResult?: UseQueryResult<ItemDtoQueryResult | undefined, Error>;
|
||||
viewSettings?: LibraryViewSettings;
|
||||
setViewSettings?: React.Dispatch<React.SetStateAction<LibraryViewSettings>>;
|
||||
}
|
||||
|
||||
const DEFAULT_LIBRARY_STATE: LibraryState = {
|
||||
isLibraryPath: false
|
||||
};
|
||||
|
||||
export const LibraryContext = createContext<LibraryState>(DEFAULT_LIBRARY_STATE);
|
||||
export const useLibrary = () => useContext(LibraryContext);
|
||||
|
||||
export const LibraryProvider: FC<PropsWithChildren<unknown>> = ({ children }) => {
|
||||
const { pathname } = useLocation();
|
||||
const { libraryId, activeTab } = useCurrentTab();
|
||||
|
||||
const route = useMemo(() => LibraryRoutes.find(({ path }) => path === pathname), [pathname]);
|
||||
const collectionType = route?.type;
|
||||
const isLibPath = isLibraryPath(pathname);
|
||||
const id = libraryId ?? undefined;
|
||||
|
||||
const content = useMemo(() => collectionType && getViewContent(collectionType, activeTab), [collectionType, activeTab]);
|
||||
const viewType = content?.viewType;
|
||||
|
||||
// Local storage requires the view type to be known upfront so default to movies if unknown
|
||||
const settingsViewType = viewType ?? LibraryTab.Movies;
|
||||
const [viewSettings, setViewSettings] =
|
||||
useLocalStorage<LibraryViewSettings>(
|
||||
getSettingsKey(settingsViewType, libraryId),
|
||||
getDefaultLibraryViewSettings(settingsViewType)
|
||||
);
|
||||
|
||||
const itemsResult = useGetItemsViewByType(
|
||||
viewType,
|
||||
libraryId,
|
||||
content?.itemType,
|
||||
viewSettings
|
||||
);
|
||||
|
||||
const state = useMemo(() => ({
|
||||
...DEFAULT_LIBRARY_STATE,
|
||||
collectionType,
|
||||
isLibraryPath: isLibPath,
|
||||
id,
|
||||
content,
|
||||
viewSettings,
|
||||
setViewSettings,
|
||||
itemsResult
|
||||
}), [collectionType, isLibPath, id, content, viewSettings, setViewSettings, itemsResult]);
|
||||
|
||||
return (
|
||||
<LibraryContext.Provider value={state}>
|
||||
{children}
|
||||
</LibraryContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
interface LibraryViewDefinition {
|
||||
@@ -9,5 +10,6 @@ interface LibraryViewDefinition {
|
||||
|
||||
export interface LibraryRoute {
|
||||
path: string,
|
||||
type: CollectionType,
|
||||
views: LibraryViewDefinition[]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
|
||||
import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
||||
|
||||
import { type ParentId, ViewMode, type LibraryViewSettings } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
export const getDefaultSortBy = (viewType: LibraryTab) => {
|
||||
if (viewType === LibraryTab.Episodes) {
|
||||
return ItemSortBy.SeriesSortName;
|
||||
}
|
||||
|
||||
return ItemSortBy.SortName;
|
||||
};
|
||||
|
||||
export const getDefaultLibraryViewSettings = (viewType: LibraryTab): LibraryViewSettings => {
|
||||
return {
|
||||
ShowTitle: true,
|
||||
ShowYear: true,
|
||||
ViewMode: viewType === LibraryTab.Songs ? ViewMode.ListView : ViewMode.GridView,
|
||||
ImageType: viewType === LibraryTab.Studios ? ImageType.Thumb : ImageType.Primary,
|
||||
CardLayout: false,
|
||||
SortBy: getDefaultSortBy(viewType),
|
||||
SortOrder: SortOrder.Ascending,
|
||||
StartIndex: 0
|
||||
};
|
||||
};
|
||||
|
||||
export const getSettingsKey = (viewType: LibraryTab, parentId: ParentId) => {
|
||||
return `${viewType} - ${parentId}`;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||
|
||||
import { LibraryTabContent } from 'types/libraryTabContent';
|
||||
|
||||
import defaultViewContent from '../constants/views/defaults';
|
||||
import viewsByKind from '../constants/views';
|
||||
|
||||
export function getViewContent(
|
||||
collectionType: CollectionType,
|
||||
viewIndex: number
|
||||
): LibraryTabContent {
|
||||
const viewContent = viewsByKind[collectionType][viewIndex];
|
||||
return {
|
||||
...defaultViewContent,
|
||||
...viewContent
|
||||
};
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api/user-library-api'
|
||||
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
|
||||
import { getLiveTvApi } from '@jellyfin/sdk/lib/utils/api/live-tv-api';
|
||||
import { getPlaystateApi } from '@jellyfin/sdk/lib/utils/api/playstate-api';
|
||||
import { keepPreviousData, useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import datetime from 'scripts/datetime';
|
||||
import globalize from 'lib/globalize';
|
||||
|
||||
@@ -395,9 +395,9 @@ const fetchGetItemsViewByType = async (
|
||||
};
|
||||
|
||||
export const useGetItemsViewByType = (
|
||||
viewType: LibraryTab,
|
||||
viewType: LibraryTab | undefined,
|
||||
parentId: ParentId,
|
||||
itemType: BaseItemKind[],
|
||||
itemType: BaseItemKind[] = [],
|
||||
libraryViewSettings: LibraryViewSettings
|
||||
) => {
|
||||
const currentApi = useApi();
|
||||
@@ -414,15 +414,15 @@ export const useGetItemsViewByType = (
|
||||
queryFn: ({ signal }) =>
|
||||
fetchGetItemsViewByType(
|
||||
currentApi,
|
||||
viewType,
|
||||
viewType!,
|
||||
parentId,
|
||||
itemType,
|
||||
libraryViewSettings,
|
||||
libraryViewSettings!,
|
||||
{ signal }
|
||||
),
|
||||
refetchOnWindowFocus: false,
|
||||
placeholderData : keepPreviousData,
|
||||
enabled: !!currentApi.api && !!currentApi.user?.Id
|
||||
&& viewType
|
||||
&& [
|
||||
LibraryTab.Movies,
|
||||
LibraryTab.Favorites,
|
||||
|
||||
@@ -28,5 +28,3 @@ export interface LibraryTabContent {
|
||||
isAlphabetPickerEnabled?: boolean;
|
||||
noItemsMessage?: string;
|
||||
}
|
||||
|
||||
export type LibraryTabMapping = Record<number, LibraryTabContent>;
|
||||
|
||||
+2
-29
@@ -1,10 +1,9 @@
|
||||
import { ItemFields } from '@jellyfin/sdk/lib/generated-client/models/item-fields';
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
|
||||
import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
||||
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import { EpisodeFilter, FeatureFilters, LibraryViewSettings, ParentId, VideoBasicFilter, ViewMode } from '../types/library';
|
||||
import { EpisodeFilter, FeatureFilters, LibraryViewSettings, VideoBasicFilter } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import type { AttributesOpts, DataAttributes } from 'types/dataAttributes';
|
||||
|
||||
@@ -142,31 +141,6 @@ export const getFiltersQuery = (
|
||||
};
|
||||
};
|
||||
|
||||
export const getSettingsKey = (viewType: LibraryTab, parentId: ParentId) => {
|
||||
return `${viewType} - ${parentId}`;
|
||||
};
|
||||
|
||||
export const getDefaultSortBy = (viewType: LibraryTab) => {
|
||||
if (viewType === LibraryTab.Episodes) {
|
||||
return ItemSortBy.SeriesSortName;
|
||||
}
|
||||
|
||||
return ItemSortBy.SortName;
|
||||
};
|
||||
|
||||
export const getDefaultLibraryViewSettings = (viewType: LibraryTab): LibraryViewSettings => {
|
||||
return {
|
||||
ShowTitle: true,
|
||||
ShowYear: true,
|
||||
ViewMode: viewType === LibraryTab.Songs ? ViewMode.ListView : ViewMode.GridView,
|
||||
ImageType: viewType === LibraryTab.Studios ? ImageType.Thumb : ImageType.Primary,
|
||||
CardLayout: false,
|
||||
SortBy: getDefaultSortBy(viewType),
|
||||
SortOrder: SortOrder.Ascending,
|
||||
StartIndex: 0
|
||||
};
|
||||
};
|
||||
|
||||
export function getDataAttributes(
|
||||
opts: AttributesOpts
|
||||
): DataAttributes {
|
||||
@@ -193,4 +167,3 @@ export function getDataAttributes(
|
||||
'data-enddate': opts.itemEndDate?.toString()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user