Migrate Movies
This commit is contained in:
parent
122c4ae600
commit
479c53eb8b
21 changed files with 1713 additions and 7 deletions
48
src/view/components/AlphaPickerContainer.tsx
Normal file
48
src/view/components/AlphaPickerContainer.tsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
|
||||
import AlphaPicker from '../../components/alphaPicker/alphaPicker';
|
||||
import { IQuery } from './type';
|
||||
|
||||
type AlphaPickerProps = {
|
||||
query: IQuery;
|
||||
reloadItems: () => void;
|
||||
};
|
||||
|
||||
const AlphaPickerContainer: FunctionComponent<AlphaPickerProps> = ({ query, reloadItems }: AlphaPickerProps) => {
|
||||
const [ alphaPicker, setAlphaPicker ] = useState<AlphaPicker>();
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
alphaPicker?.updateControls(query);
|
||||
|
||||
useEffect(() => {
|
||||
const alphaPickerElement = element.current?.querySelector('.alphaPicker');
|
||||
|
||||
if (alphaPickerElement) {
|
||||
alphaPickerElement.addEventListener('alphavaluechanged', (e) => {
|
||||
const newValue = (e as CustomEvent).detail.value;
|
||||
if (newValue === '#') {
|
||||
query.NameLessThan = 'A';
|
||||
delete query.NameStartsWith;
|
||||
} else {
|
||||
query.NameStartsWith = newValue;
|
||||
delete query.NameLessThan;
|
||||
}
|
||||
query.StartIndex = 0;
|
||||
reloadItems();
|
||||
});
|
||||
setAlphaPicker(new AlphaPicker({
|
||||
element: alphaPickerElement,
|
||||
valueChangeEvent: 'click'
|
||||
}));
|
||||
|
||||
alphaPickerElement.classList.add('alphaPicker-fixed-right');
|
||||
}
|
||||
}, [query, reloadItems, setAlphaPicker]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='alphaPicker alphaPicker-fixed alphaPicker-vertical alphabetPicker-right' />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlphaPickerContainer;
|
51
src/view/components/Filter.tsx
Normal file
51
src/view/components/Filter.tsx
Normal file
|
@ -0,0 +1,51 @@
|
|||
import React, { FunctionComponent, useCallback, useEffect, useRef } from 'react';
|
||||
import { Events } from 'jellyfin-apiclient';
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
import { IQuery } from './type';
|
||||
|
||||
type FilterProps = {
|
||||
query: IQuery;
|
||||
reloadItems: () => void;
|
||||
}
|
||||
|
||||
const Filter: FunctionComponent<FilterProps> = ({ query, reloadItems }: FilterProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const showFilterMenu = useCallback(() => {
|
||||
import('../../components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => {
|
||||
const filterDialog = new filterDialogFactory({
|
||||
query: query,
|
||||
mode: 'movies',
|
||||
serverId: window.ApiClient.serverId()
|
||||
});
|
||||
Events.on(filterDialog, 'filterchange', () => {
|
||||
query.StartIndex = 0;
|
||||
reloadItems();
|
||||
});
|
||||
filterDialog.show();
|
||||
});
|
||||
}, [query, reloadItems]);
|
||||
|
||||
useEffect(() => {
|
||||
const btnFilter = element.current?.querySelector('.btnFilter');
|
||||
|
||||
if (btnFilter) {
|
||||
btnFilter.addEventListener('click', () => {
|
||||
showFilterMenu();
|
||||
});
|
||||
}
|
||||
}, [showFilterMenu]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnFilter autoSize'
|
||||
title='Filter'
|
||||
icon='material-icons filter_list'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Filter;
|
165
src/view/components/GenresItemsContainer.tsx
Normal file
165
src/view/components/GenresItemsContainer.tsx
Normal file
|
@ -0,0 +1,165 @@
|
|||
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import lazyLoader from '../../components/lazyLoader/lazyLoaderIntersectionObserver';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import { appRouter } from '../../components/appRouter';
|
||||
import escapeHTML from 'escape-html';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import { IQuery } from './type';
|
||||
|
||||
type GenresItemsContainerProps = {
|
||||
topParentId?: string | null;
|
||||
getCurrentViewStyle: () => string;
|
||||
query: IQuery;
|
||||
itemsResult?: BaseItemDtoQueryResult;
|
||||
}
|
||||
|
||||
const GenresItemsContainer: FunctionComponent<GenresItemsContainerProps> = ({ topParentId, getCurrentViewStyle, query, itemsResult = {} }: GenresItemsContainerProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const enableScrollX = useCallback(() => {
|
||||
return !layoutManager.desktop;
|
||||
}, []);
|
||||
|
||||
const getPortraitShape = useCallback(() => {
|
||||
return enableScrollX() ? 'overflowPortrait' : 'portrait';
|
||||
}, [enableScrollX]);
|
||||
|
||||
const getThumbShape = useCallback(() => {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}, [enableScrollX]);
|
||||
|
||||
const fillItemsContainer = useCallback((entry) => {
|
||||
const elem = entry.target;
|
||||
const id = elem.getAttribute('data-id');
|
||||
const viewStyle = getCurrentViewStyle();
|
||||
let limit = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 5 : 9;
|
||||
|
||||
if (enableScrollX()) {
|
||||
limit = 10;
|
||||
}
|
||||
|
||||
const enableImageTypes = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 'Primary,Backdrop,Thumb' : 'Primary';
|
||||
const query = {
|
||||
SortBy: 'Random',
|
||||
SortOrder: 'Ascending',
|
||||
IncludeItemTypes: 'Movie',
|
||||
Recursive: true,
|
||||
Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo',
|
||||
ImageTypeLimit: 1,
|
||||
EnableImageTypes: enableImageTypes,
|
||||
Limit: limit,
|
||||
GenreIds: id,
|
||||
EnableTotalRecordCount: false,
|
||||
ParentId: topParentId
|
||||
};
|
||||
window.ApiClient.getItems(window.ApiClient.getCurrentUserId(), query).then((result) => {
|
||||
const items = result.Items || [];
|
||||
if (viewStyle == 'Thumb') {
|
||||
cardBuilder.buildCards(items, {
|
||||
itemsContainer: elem,
|
||||
shape: getThumbShape(),
|
||||
preferThumb: true,
|
||||
showTitle: true,
|
||||
scalable: true,
|
||||
centerText: true,
|
||||
overlayMoreButton: true,
|
||||
allowBottomPadding: false
|
||||
});
|
||||
} else if (viewStyle == 'ThumbCard') {
|
||||
cardBuilder.buildCards(items, {
|
||||
itemsContainer: elem,
|
||||
shape: getThumbShape(),
|
||||
preferThumb: true,
|
||||
showTitle: true,
|
||||
scalable: true,
|
||||
centerText: false,
|
||||
cardLayout: true,
|
||||
showYear: true
|
||||
});
|
||||
} else if (viewStyle == 'PosterCard') {
|
||||
cardBuilder.buildCards(items, {
|
||||
itemsContainer: elem,
|
||||
shape: getPortraitShape(),
|
||||
showTitle: true,
|
||||
scalable: true,
|
||||
centerText: false,
|
||||
cardLayout: true,
|
||||
showYear: true
|
||||
});
|
||||
} else if (viewStyle == 'Poster') {
|
||||
cardBuilder.buildCards(items, {
|
||||
itemsContainer: elem,
|
||||
shape: getPortraitShape(),
|
||||
scalable: true,
|
||||
overlayMoreButton: true,
|
||||
allowBottomPadding: true,
|
||||
showTitle: true,
|
||||
centerText: true,
|
||||
showYear: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [enableScrollX, getCurrentViewStyle, getPortraitShape, getThumbShape, topParentId]);
|
||||
|
||||
useEffect(() => {
|
||||
const elem = element.current?.querySelector('#items') as HTMLDivElement;
|
||||
let html = '';
|
||||
const items = itemsResult.Items || [];
|
||||
|
||||
for (let i = 0, length = items.length; i < length; i++) {
|
||||
const item = items[i];
|
||||
|
||||
html += '<div class="verticalSection">';
|
||||
html += '<div class="sectionTitleContainer sectionTitleContainer-cards padded-left">';
|
||||
html += '<a is="emby-linkbutton" href="' + appRouter.getRouteUrl(item, {
|
||||
context: 'movies',
|
||||
parentId: topParentId
|
||||
}) + '" class="more button-flat button-flat-mini sectionTitleTextButton btnMoreFromGenre' + item.Id + '">';
|
||||
html += '<h2 class="sectionTitle sectionTitle-cards">';
|
||||
html += escapeHTML(item.Name);
|
||||
html += '</h2>';
|
||||
html += '<span class="material-icons chevron_right" aria-hidden="true"></span>';
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
if (enableScrollX()) {
|
||||
let scrollXClass = 'scrollX hiddenScrollX';
|
||||
|
||||
if (layoutManager.tv) {
|
||||
scrollXClass += 'smoothScrollX padded-top-focusscale padded-bottom-focusscale';
|
||||
}
|
||||
|
||||
html += '<div is="emby-itemscontainer" class="itemsContainer ' + scrollXClass + ' lazy padded-left padded-right" data-id="' + item.Id + '">';
|
||||
} else {
|
||||
html += '<div is="emby-itemscontainer" class="itemsContainer vertical-wrap lazy padded-left padded-right" data-id="' + item.Id + '">';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
if (!itemsResult.Items?.length) {
|
||||
html = '';
|
||||
|
||||
html += '<div class="noItemsMessage centerMessage">';
|
||||
html += '<h1>' + globalize.translate('MessageNothingHere') + '</h1>';
|
||||
html += '<p>' + globalize.translate('MessageNoGenresAvailable') + '</p>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
elem.innerHTML = html;
|
||||
lazyLoader.lazyChildren(elem, fillItemsContainer);
|
||||
}, [getCurrentViewStyle, query.SortBy, itemsResult.Items, fillItemsContainer, topParentId, enableScrollX]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div id='items'></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GenresItemsContainer;
|
110
src/view/components/ItemsContainer.tsx
Normal file
110
src/view/components/ItemsContainer.tsx
Normal file
|
@ -0,0 +1,110 @@
|
|||
import { BaseItemDto } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useEffect, useRef } from 'react';
|
||||
|
||||
import ItemsContainerElement from '../../elements/ItemsContainerElement';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import listview from '../../components/listview/listview';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import { IQuery } from './type';
|
||||
|
||||
type ItemsContainerProps = {
|
||||
getCurrentViewStyle: () => string;
|
||||
query: IQuery;
|
||||
items?: BaseItemDto[] | null;
|
||||
noItemsMessage?: string;
|
||||
}
|
||||
|
||||
const ItemsContainer: FunctionComponent<ItemsContainerProps> = ({ getCurrentViewStyle, query, items = [], noItemsMessage }: ItemsContainerProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let html;
|
||||
const viewStyle = getCurrentViewStyle();
|
||||
if (viewStyle == 'Thumb') {
|
||||
html = cardBuilder.getCardsHtml(items, {
|
||||
items: items,
|
||||
shape: 'backdrop',
|
||||
preferThumb: true,
|
||||
context: 'movies',
|
||||
lazy: true,
|
||||
overlayPlayButton: true,
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true
|
||||
});
|
||||
} else if (viewStyle == 'ThumbCard') {
|
||||
html = cardBuilder.getCardsHtml(items, {
|
||||
items: items,
|
||||
shape: 'backdrop',
|
||||
preferThumb: true,
|
||||
context: 'movies',
|
||||
lazy: true,
|
||||
cardLayout: true,
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true
|
||||
});
|
||||
} else if (viewStyle == 'Banner') {
|
||||
html = cardBuilder.getCardsHtml(items, {
|
||||
items: items,
|
||||
shape: 'banner',
|
||||
preferBanner: true,
|
||||
context: 'movies',
|
||||
lazy: true
|
||||
});
|
||||
} else if (viewStyle == 'List') {
|
||||
html = listview.getListViewHtml({
|
||||
items: items,
|
||||
context: 'movies',
|
||||
sortBy: query.SortBy
|
||||
});
|
||||
} else if (viewStyle == 'PosterCard') {
|
||||
html = cardBuilder.getCardsHtml(items, {
|
||||
items: items,
|
||||
shape: 'portrait',
|
||||
context: 'movies',
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true,
|
||||
lazy: true,
|
||||
cardLayout: true
|
||||
});
|
||||
} else {
|
||||
html = cardBuilder.getCardsHtml(items, {
|
||||
items: items,
|
||||
shape: 'portrait',
|
||||
context: 'movies',
|
||||
overlayPlayButton: true,
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true
|
||||
});
|
||||
}
|
||||
|
||||
if (!items?.length) {
|
||||
html = '';
|
||||
|
||||
html += '<div class="noItemsMessage centerMessage">';
|
||||
html += '<h1>' + globalize.translate('MessageNothingHere') + '</h1>';
|
||||
html += '<p>' + globalize.translate(noItemsMessage) + '</p>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
const itemsContainer = element.current?.querySelector('.itemsContainer') as HTMLDivElement;
|
||||
itemsContainer.innerHTML = html;
|
||||
imageLoader.lazyChildren(itemsContainer);
|
||||
}, [getCurrentViewStyle, query.SortBy, items, noItemsMessage]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<ItemsContainerElement
|
||||
id=''
|
||||
className='itemsContainer vertical-wrap centered padded-left padded-right'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemsContainer;
|
35
src/view/components/NewCollection.tsx
Normal file
35
src/view/components/NewCollection.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import React, { FunctionComponent, useEffect, useRef } from 'react';
|
||||
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
|
||||
const NewCollection: FunctionComponent = () => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const btnNewCollection = element.current?.querySelector('.btnNewCollection') as HTMLButtonElement;
|
||||
if (btnNewCollection) {
|
||||
btnNewCollection.addEventListener('click', () => {
|
||||
import('../../components/collectionEditor/collectionEditor').then(({ default: collectionEditor }) => {
|
||||
const serverId = window.ApiClient.serverId();
|
||||
new collectionEditor({
|
||||
items: [],
|
||||
serverId: serverId
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnNewCollection autoSize'
|
||||
title='Add'
|
||||
icon='material-icons add'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewCollection;
|
65
src/view/components/Pagination.tsx
Normal file
65
src/view/components/Pagination.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useEffect, useRef } from 'react';
|
||||
import libraryBrowser from '../../scripts/libraryBrowser';
|
||||
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import { IQuery } from './type';
|
||||
|
||||
type PaginationProps = {
|
||||
query: IQuery;
|
||||
itemsResult?: BaseItemDtoQueryResult;
|
||||
reloadItems: () => void;
|
||||
}
|
||||
|
||||
const Pagination: FunctionComponent<PaginationProps> = ({ query, itemsResult = {}, reloadItems }: PaginationProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
function onNextPageClick() {
|
||||
if (userSettings.libraryPageSize(undefined) > 0) {
|
||||
query.StartIndex += query.Limit;
|
||||
}
|
||||
reloadItems();
|
||||
}
|
||||
|
||||
function onPreviousPageClick() {
|
||||
if (userSettings.libraryPageSize(undefined) > 0) {
|
||||
query.StartIndex = Math.max(0, query.StartIndex - query.Limit);
|
||||
}
|
||||
reloadItems();
|
||||
}
|
||||
const pagingHtml = libraryBrowser.getQueryPagingHtml({
|
||||
startIndex: query.StartIndex,
|
||||
limit: query.Limit,
|
||||
totalRecordCount: itemsResult.TotalRecordCount,
|
||||
showLimit: false,
|
||||
updatePageSizeSetting: false,
|
||||
addLayoutButton: false,
|
||||
sortButton: false,
|
||||
filterButton: false
|
||||
});
|
||||
|
||||
const paging = element.current?.querySelector('.paging') as HTMLDivElement;
|
||||
paging.innerHTML = pagingHtml;
|
||||
|
||||
const btnNextPage = element.current?.querySelector('.btnNextPage') as HTMLButtonElement;
|
||||
if (btnNextPage) {
|
||||
btnNextPage.addEventListener('click', onNextPageClick);
|
||||
}
|
||||
|
||||
const btnPreviousPage = element.current?.querySelector('.btnPreviousPage') as HTMLButtonElement;
|
||||
if (btnPreviousPage) {
|
||||
btnPreviousPage.addEventListener('click', onPreviousPageClick);
|
||||
}
|
||||
}, [itemsResult, query, reloadItems]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div
|
||||
className='paging'
|
||||
/>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default Pagination;
|
45
src/view/components/SelectView.tsx
Normal file
45
src/view/components/SelectView.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import React, { FunctionComponent, useEffect, useRef } from 'react';
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
|
||||
import libraryBrowser from '../../scripts/libraryBrowser';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import { IQuery } from './type';
|
||||
|
||||
type SelectViewProps = {
|
||||
getCurrentViewStyle: () => string;
|
||||
query: IQuery;
|
||||
savedViewKey: string;
|
||||
onViewStyleChange: () => void;
|
||||
reloadItems: () => void;
|
||||
}
|
||||
|
||||
const SelectView: FunctionComponent<SelectViewProps> = ({ getCurrentViewStyle, savedViewKey, query, onViewStyleChange, reloadItems }: SelectViewProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const btnSelectView = element.current?.querySelector('.btnSelectView') as HTMLButtonElement;
|
||||
btnSelectView.addEventListener('click', (e) => {
|
||||
libraryBrowser.showLayoutMenu(e.target, getCurrentViewStyle(), 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
|
||||
});
|
||||
btnSelectView.addEventListener('layoutchange', (e) => {
|
||||
const viewStyle = (e as CustomEvent).detail.viewStyle;
|
||||
userSettings.set(savedViewKey, viewStyle, false);
|
||||
query.StartIndex = 0;
|
||||
onViewStyleChange();
|
||||
reloadItems();
|
||||
});
|
||||
}, [getCurrentViewStyle, onViewStyleChange, query, reloadItems, savedViewKey]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnSelectView autoSize'
|
||||
title='ButtonSelectView'
|
||||
icon='material-icons view_comfy'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectView;
|
44
src/view/components/Shuffle.tsx
Normal file
44
src/view/components/Shuffle.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
|
||||
type ShuffleProps = {
|
||||
itemsResult?: BaseItemDtoQueryResult;
|
||||
topParentId: string | null;
|
||||
}
|
||||
|
||||
const Shuffle: FunctionComponent<ShuffleProps> = ({ itemsResult = {}, topParentId }: ShuffleProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const shuffle = useCallback(() => {
|
||||
window.ApiClient.getItem(
|
||||
window.ApiClient.getCurrentUserId(),
|
||||
topParentId as string
|
||||
).then((item) => {
|
||||
playbackManager.shuffle(item);
|
||||
});
|
||||
}, [topParentId]);
|
||||
|
||||
useEffect(() => {
|
||||
const btnShuffle = element.current?.querySelector('.btnShuffle') as HTMLButtonElement;
|
||||
btnShuffle.classList.toggle('hide', typeof itemsResult.TotalRecordCount === 'number' && itemsResult.TotalRecordCount < 1);
|
||||
if (btnShuffle) {
|
||||
btnShuffle.addEventListener('click', shuffle);
|
||||
}
|
||||
}, [itemsResult.TotalRecordCount, shuffle]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnShuffle autoSize hide'
|
||||
title='Shuffle'
|
||||
icon='material-icons shuffle'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Shuffle;
|
48
src/view/components/Sort.tsx
Normal file
48
src/view/components/Sort.tsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
import React, { FunctionComponent, useEffect, useRef } from 'react';
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
import libraryBrowser from '../../scripts/libraryBrowser';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import { IQuery } from './type';
|
||||
|
||||
type SortProps = {
|
||||
SortMenuOptions: () => { name: string; id: string}[];
|
||||
query: IQuery;
|
||||
savedQueryKey: string;
|
||||
reloadItems: () => void;
|
||||
}
|
||||
|
||||
const Sort: FunctionComponent<SortProps> = ({ SortMenuOptions, query, savedQueryKey, reloadItems }: SortProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const btnSort = element.current?.querySelector('.btnSort');
|
||||
|
||||
if (btnSort) {
|
||||
btnSort.addEventListener('click', (e) => {
|
||||
libraryBrowser.showSortMenu({
|
||||
items: SortMenuOptions(),
|
||||
callback: () => {
|
||||
query.StartIndex = 0;
|
||||
userSettings.saveQuerySettings(savedQueryKey, query);
|
||||
reloadItems();
|
||||
},
|
||||
query: query,
|
||||
button: e.target
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [SortMenuOptions, query, reloadItems, savedQueryKey]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnSort autoSize'
|
||||
title='Sort'
|
||||
icon='material-icons sort_by_alpha'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sort;
|
15
src/view/components/type.ts
Normal file
15
src/view/components/type.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export type IQuery = {
|
||||
SortBy?: string;
|
||||
SortOrder?: string;
|
||||
IncludeItemTypes?: string;
|
||||
Recursive?: boolean;
|
||||
Fields?: string;
|
||||
ImageTypeLimit?: number;
|
||||
EnableImageTypes?: string;
|
||||
StartIndex: number;
|
||||
ParentId?: string | null;
|
||||
IsFavorite?: boolean;
|
||||
Limit:number;
|
||||
NameLessThan?: string;
|
||||
NameStartsWith?: string;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue