Tunio Desktop client
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

251 lines
8.6 KiB

import React, { useRef, useState } from 'react';
import { useQuery, useQueryClient } from 'react-query';
import { useHistory } from 'react-router-dom';
import { Form, Whisper } from 'rsuite';
import { useTranslation } from 'react-i18next';
import useSearchQuery from '../../hooks/useSearchQuery';
import ListViewType from '../viewtypes/ListViewType';
import GenericPage from '../layout/GenericPage';
import GenericPageHeader from '../layout/GenericPageHeader';
import GridViewType from '../viewtypes/GridViewType';
import { StyledButton, StyledInput, StyledInputGroup, StyledTag } from '../shared/styled';
import { errorMessages, isFailedResponse } from '../../shared/utils';
import { notifyToast } from '../shared/toast';
import { AddPlaylistButton, FilterButton } from '../shared/ToolbarButtons';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { apiController } from '../../api/controller';
import useColumnSort from '../../hooks/useColumnSort';
import { Item, Server } from '../../types';
import { setSort } from '../../redux/playlistSlice';
import ColumnSortPopover from '../shared/ColumnSortPopover';
import useListClickHandler from '../../hooks/useListClickHandler';
import Popup from '../shared/Popup';
import { settings } from '../shared/setDefaultSettings';
const PlaylistList = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const history = useHistory();
const queryClient = useQueryClient();
const config = useAppSelector((state) => state.config);
const misc = useAppSelector((state) => state.misc);
const playlist = useAppSelector((state) => state.playlist);
const playlistTriggerRef = useRef<any>();
const [newPlaylistName, setNewPlaylistName] = useState('');
const [viewType, setViewType] = useState(settings.get('playlistViewType') || 'list');
const {
isLoading,
isError,
data: playlists,
error,
}: any = useQuery(['playlists'], () =>
apiController({ serverType: config.serverType, endpoint: 'getPlaylists' })
);
const filteredData = useSearchQuery(misc.searchQuery, playlists, ['title', 'comment', 'owner']);
const { sortedData, sortColumns } = useColumnSort(
playlists,
Item.Playlist,
playlist.active.list.sort
);
const handleCreatePlaylist = async (name: string) => {
try {
const res = await apiController({
serverType: config.serverType,
endpoint: 'createPlaylist',
args: { name },
});
if (isFailedResponse(res)) {
notifyToast('error', errorMessages(res)[0]);
} else {
await queryClient.refetchQueries(['playlists'], {
active: true,
});
notifyToast('success', `Playlist "${name}" created!`);
}
} catch (err) {
notifyToast('error', err);
}
};
const { handleRowClick, handleRowDoubleClick } = useListClickHandler({
doubleClick: (rowData: any) => history.push(`playlist/${rowData.id}`),
});
if (isError) {
return <span>{t('Error: {{error}}', { error: error.message })}</span>;
}
return (
<GenericPage
hideDivider
header={
<GenericPageHeader
title={
<>
{t('Playlists')}{' '}
<StyledTag style={{ verticalAlign: 'middle', cursor: 'default' }}>
{sortedData?.length || '...'}
</StyledTag>
</>
}
sidetitle={
<>
<ColumnSortPopover
sortColumns={sortColumns}
sortColumn={playlist.active.list.sort.column}
sortType={playlist.active.list.sort.type}
disabledItemValues={
config.serverType === Server.Jellyfin ? ['changed', 'owner', 'public'] : []
}
clearSortType={() =>
dispatch(
setSort({
type: 'list',
value: {
...playlist.active.list.sort,
column: undefined,
},
})
)
}
setSortType={(e: string) =>
dispatch(
setSort({
type: 'list',
value: {
...playlist.active.list.sort,
type: e,
},
})
)
}
setSortColumn={(e: string) =>
dispatch(
setSort({
type: 'list',
value: {
...playlist.active.list.sort,
column: e,
},
})
)
}
>
<FilterButton
size="sm"
appearance={playlist.active.list.sort.column ? 'primary' : 'subtle'}
/>
</ColumnSortPopover>
</>
}
subtitle={
<Whisper
ref={playlistTriggerRef}
enterable
placement="auto"
trigger="click"
speaker={
<Popup>
<Form>
<StyledInputGroup>
<StyledInput
placeholder={t('Enter name...')}
value={newPlaylistName}
onChange={(e: string) => setNewPlaylistName(e)}
/>
</StyledInputGroup>
<br />
<StyledButton
size="sm"
type="submit"
block
loading={false}
appearance="primary"
onClick={() => {
handleCreatePlaylist(newPlaylistName);
playlistTriggerRef.current.close();
}}
>
{t('Create')}
</StyledButton>
</Form>
</Popup>
}
>
<AddPlaylistButton
size="sm"
onClick={() =>
playlistTriggerRef.current.state.isOverlayShown
? playlistTriggerRef.current.close()
: playlistTriggerRef.current.open()
}
/>
</Whisper>
}
showViewTypeButtons
viewTypeSetting="playlist"
handleListClick={() => setViewType('list')}
handleGridClick={() => setViewType('grid')}
/>
}
>
{viewType === 'list' && (
<ListViewType
data={misc.searchQuery === '' ? sortedData : filteredData}
loading={isLoading}
handleRowClick={handleRowClick}
handleRowDoubleClick={handleRowDoubleClick}
tableColumns={config.lookAndFeel.listView.playlist.columns}
rowHeight={config.lookAndFeel.listView.playlist.rowHeight}
fontSize={config.lookAndFeel.listView.playlist.fontSize}
cacheImages={{
enabled: settings.get('cacheImages'),
cacheType: 'playlist',
cacheIdProperty: 'id',
}}
page="playlistListPage"
listType="playlist"
virtualized
disabledContextMenuOptions={[
'moveSelectedTo',
'addToFavorites',
'removeFromFavorites',
'removeSelected',
'viewInFolder',
]}
initialScrollOffset={Number(localStorage.getItem('scroll_list_playlistList'))}
onScroll={(scrollIndex: number) => {
localStorage.setItem('scroll_list_playlistList', String(Math.abs(scrollIndex)));
}}
/>
)}
{viewType === 'grid' && (
<GridViewType
data={misc.searchQuery === '' ? sortedData : filteredData}
loading={isLoading}
cardTitle={{
prefix: 'playlist',
property: 'title',
urlProperty: 'id',
}}
cardSubtitle={{
prefix: 'playlist',
property: 'songCount',
unit: ' tracks',
}}
playClick={{ type: 'playlist', idProperty: 'id' }}
size={config.lookAndFeel.gridView.cardSize}
cacheType="playlist"
initialScrollOffset={Number(localStorage.getItem('scroll_grid_playlistList'))}
onScroll={(scrollIndex: number) => {
localStorage.setItem('scroll_grid_playlistList', String(scrollIndex));
}}
/>
)}
</GenericPage>
);
};
export default PlaylistList;