mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-05 01:19:29 +00:00
add playlist subscribed filter, align filter UIUX
This commit is contained in:
@@ -36,7 +36,8 @@ export type UserConfigType = {
|
||||
hide_watched: boolean | null;
|
||||
file_size_unit: 'binary' | 'metric';
|
||||
show_ignored_only: boolean;
|
||||
show_subed_only: boolean;
|
||||
show_subed_only: boolean | null;
|
||||
show_subed_only_playlists: boolean | null;
|
||||
show_help_text: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,11 +9,13 @@ export type ChannelsListResponse = {
|
||||
config?: ConfigType;
|
||||
};
|
||||
|
||||
const loadChannelList = async (page: number, showSubscribed: boolean) => {
|
||||
const loadChannelList = async (page: number, showSubscribed: boolean | null) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
if (page) searchParams.append('page', page.toString());
|
||||
if (showSubscribed) searchParams.append('filter', 'subscribed');
|
||||
if (showSubscribed !== null) {
|
||||
searchParams.append('filter', showSubscribed ? 'subscribed' : 'unsubscribed');
|
||||
}
|
||||
|
||||
const endpoint = `/api/channel/${searchParams.toString() ? `?${searchParams.toString()}` : ''}`;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ type PlaylistCategoryType = 'regular' | 'custom';
|
||||
type LoadPlaylistListProps = {
|
||||
channel?: string;
|
||||
page?: number | undefined;
|
||||
subscribed?: boolean;
|
||||
subscribed?: boolean | null;
|
||||
type?: PlaylistCategoryType;
|
||||
};
|
||||
|
||||
@@ -21,7 +21,8 @@ const loadPlaylistList = async ({ channel, page, subscribed, type }: LoadPlaylis
|
||||
|
||||
if (channel) searchParams.append('channel', channel);
|
||||
if (page) searchParams.append('page', page.toString());
|
||||
if (subscribed) searchParams.append('subscribed', subscribed.toString());
|
||||
if (subscribed !== undefined && subscribed !== null)
|
||||
searchParams.append('subscribed', subscribed.toString());
|
||||
if (type) searchParams.append('type', type);
|
||||
|
||||
const endpoint = `/api/playlist/${searchParams.toString() ? `?${searchParams.toString()}` : ''}`;
|
||||
|
||||
@@ -7,6 +7,7 @@ import ScrollToTopOnNavigate from '../components/ScrollToTop';
|
||||
import loadPlaylistList, { PlaylistsResponseType } from '../api/loader/loadPlaylistList';
|
||||
import iconGridView from '/img/icon-gridview.svg';
|
||||
import iconListView from '/img/icon-listview.svg';
|
||||
import iconFilter from '/img/icon-filter.svg';
|
||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||
import { ApiResponseType } from '../functions/APIClient';
|
||||
@@ -18,6 +19,7 @@ const ChannelPlaylist = () => {
|
||||
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
||||
|
||||
const [refreshPlaylists, setRefreshPlaylists] = useState(false);
|
||||
const [showFilterItems, setShowFilterItems] = useState(false);
|
||||
|
||||
const [playlistsResponse, setPlaylistsResponse] =
|
||||
useState<ApiResponseType<PlaylistsResponseType>>();
|
||||
@@ -28,7 +30,7 @@ const ChannelPlaylist = () => {
|
||||
const pagination = playlistsResponseData?.paginate;
|
||||
|
||||
const viewStyle = userConfig.view_style_playlist;
|
||||
const showSubedOnly = userConfig.show_subed_only;
|
||||
const showSubedOnly = userConfig.show_subed_only_playlists;
|
||||
|
||||
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||
const updatedUserConfig = await updateUserConfig(config);
|
||||
@@ -58,30 +60,35 @@ const ChannelPlaylist = () => {
|
||||
<ScrollToTopOnNavigate />
|
||||
<div className="boxed-content">
|
||||
<div className="view-controls">
|
||||
<div className="toggle">
|
||||
<span>Show subscribed only:</span>
|
||||
<div className="toggleBox">
|
||||
<input
|
||||
checked={showSubedOnly}
|
||||
onChange={() => {
|
||||
handleUserConfigUpdate({ show_subed_only: !showSubedOnly });
|
||||
setRefreshPlaylists(true);
|
||||
}}
|
||||
type="checkbox"
|
||||
/>
|
||||
{!showSubedOnly && (
|
||||
<label htmlFor="" className="ofbtn">
|
||||
Off
|
||||
</label>
|
||||
)}
|
||||
{showSubedOnly && (
|
||||
<label htmlFor="" className="onbtn">
|
||||
On
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="view-icons">
|
||||
{showFilterItems && (
|
||||
<div>
|
||||
<span>Filter:</span>
|
||||
<select
|
||||
value={
|
||||
userConfig.show_subed_only_playlists === null
|
||||
? ''
|
||||
: userConfig.show_subed_only_playlists.toString()
|
||||
}
|
||||
onChange={event => {
|
||||
handleUserConfigUpdate({
|
||||
show_subed_only_playlists:
|
||||
event.target.value === '' ? null : event.target.value === 'true',
|
||||
});
|
||||
setRefreshPlaylists(true);
|
||||
}}
|
||||
>
|
||||
<option value="">All subscribe state</option>
|
||||
<option value="true">Subscribed only</option>
|
||||
<option value="false">Unsubscribed only</option>
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
src={iconFilter}
|
||||
alt="icon filter"
|
||||
onClick={() => setShowFilterItems(!showFilterItems)}
|
||||
/>
|
||||
<img
|
||||
src={iconGridView}
|
||||
onClick={() => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import loadChannelList, { ChannelsListResponse } from '../api/loader/loadChannel
|
||||
import iconGridView from '/img/icon-gridview.svg';
|
||||
import iconListView from '/img/icon-listview.svg';
|
||||
import iconAdd from '/img/icon-add.svg';
|
||||
import iconFilter from '/img/icon-filter.svg';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Pagination from '../components/Pagination';
|
||||
import { OutletContextType } from './Base';
|
||||
@@ -52,6 +53,7 @@ const Channels = () => {
|
||||
useState<ApiResponseType<ChannelsListResponse>>();
|
||||
const [showAddForm, setShowAddForm] = useState(false);
|
||||
const [refresh, setRefresh] = useState(true);
|
||||
const [showFilterItems, setShowFilterItems] = useState(false);
|
||||
const [showNotification, setShowNotification] = useState(false);
|
||||
const [channelsToSubscribeTo, setChannelsToSubscribeTo] = useState('');
|
||||
|
||||
@@ -144,31 +146,34 @@ const Channels = () => {
|
||||
/>
|
||||
|
||||
<div className="view-controls">
|
||||
<div className="toggle">
|
||||
<span>Show subscribed only:</span>
|
||||
<div className="toggleBox">
|
||||
<input
|
||||
id="show_subed_only"
|
||||
onChange={async () => {
|
||||
handleUserConfigUpdate({ show_subed_only: !userConfig.show_subed_only });
|
||||
setRefresh(true);
|
||||
}}
|
||||
type="checkbox"
|
||||
checked={userConfig.show_subed_only}
|
||||
/>
|
||||
{!userConfig.show_subed_only && (
|
||||
<label htmlFor="" className="ofbtn">
|
||||
Off
|
||||
</label>
|
||||
)}
|
||||
{userConfig.show_subed_only && (
|
||||
<label htmlFor="" className="onbtn">
|
||||
On
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="view-icons">
|
||||
{showFilterItems && (
|
||||
<div>
|
||||
<span>Filter:</span>
|
||||
<select
|
||||
value={
|
||||
userConfig.show_subed_only === null ? '' : userConfig.show_subed_only.toString()
|
||||
}
|
||||
onChange={event => {
|
||||
handleUserConfigUpdate({
|
||||
show_subed_only:
|
||||
event.target.value === '' ? null : event.target.value === 'true',
|
||||
});
|
||||
setRefresh(true);
|
||||
}}
|
||||
>
|
||||
<option value="">All subscribe state</option>
|
||||
<option value="true">Subscribed only</option>
|
||||
<option value="false">Unsubscribed only</option>
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
src={iconFilter}
|
||||
alt="icon filter"
|
||||
onClick={() => setShowFilterItems(!showFilterItems)}
|
||||
/>
|
||||
|
||||
<img
|
||||
src={iconGridView}
|
||||
onClick={() => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useOutletContext } from 'react-router-dom';
|
||||
import iconAdd from '/img/icon-add.svg';
|
||||
import iconGridView from '/img/icon-gridview.svg';
|
||||
import iconListView from '/img/icon-listview.svg';
|
||||
import iconFilter from '/img/icon-filter.svg';
|
||||
|
||||
import { OutletContextType } from './Base';
|
||||
import loadPlaylistList, { PlaylistsResponseType } from '../api/loader/loadPlaylistList';
|
||||
@@ -28,6 +29,7 @@ const Playlists = () => {
|
||||
const [showAddForm, setShowAddForm] = useState(false);
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [showNotification, setShowNotification] = useState(false);
|
||||
const [showFilterItems, setShowFilterItems] = useState(false);
|
||||
const [playlistsToAddText, setPlaylistsToAddText] = useState('');
|
||||
const [customPlaylistsToAddText, setCustomPlaylistsToAddText] = useState('');
|
||||
|
||||
@@ -39,7 +41,7 @@ const Playlists = () => {
|
||||
const pagination = playlistResponseData?.paginate;
|
||||
|
||||
const viewStyle = userConfig.view_style_playlist;
|
||||
const showSubedOnly = userConfig.show_subed_only;
|
||||
const showSubedOnly = userConfig.show_subed_only_playlists;
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -53,7 +55,7 @@ const Playlists = () => {
|
||||
setShowNotification(false);
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [refresh, userConfig.show_subed_only, currentPage, pagination?.current_page]);
|
||||
}, [refresh, userConfig.show_subed_only_playlists, currentPage, pagination?.current_page]);
|
||||
|
||||
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||
const updatedUserConfig = await updateUserConfig(config);
|
||||
@@ -150,29 +152,35 @@ const Playlists = () => {
|
||||
/>
|
||||
|
||||
<div className="view-controls">
|
||||
<div className="toggle">
|
||||
<span>Show subscribed only:</span>
|
||||
<div className="toggleBox">
|
||||
<input
|
||||
checked={showSubedOnly}
|
||||
onChange={() => {
|
||||
handleUserConfigUpdate({ show_subed_only: !showSubedOnly });
|
||||
}}
|
||||
type="checkbox"
|
||||
/>
|
||||
{!showSubedOnly && (
|
||||
<label htmlFor="" className="ofbtn">
|
||||
Off
|
||||
</label>
|
||||
)}
|
||||
{showSubedOnly && (
|
||||
<label htmlFor="" className="onbtn">
|
||||
On
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="view-icons">
|
||||
{showFilterItems && (
|
||||
<div>
|
||||
<span>Filter:</span>
|
||||
<select
|
||||
value={
|
||||
userConfig.show_subed_only_playlists === null
|
||||
? ''
|
||||
: userConfig.show_subed_only_playlists.toString()
|
||||
}
|
||||
onChange={event => {
|
||||
handleUserConfigUpdate({
|
||||
show_subed_only_playlists:
|
||||
event.target.value === '' ? null : event.target.value === 'true',
|
||||
});
|
||||
setRefresh(true);
|
||||
}}
|
||||
>
|
||||
<option value="">All subscribe state</option>
|
||||
<option value="true">Subscribed only</option>
|
||||
<option value="false">Unsubscribed only</option>
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
src={iconFilter}
|
||||
alt="icon filter"
|
||||
onClick={() => setShowFilterItems(!showFilterItems)}
|
||||
/>
|
||||
<img
|
||||
src={iconGridView}
|
||||
onClick={() => {
|
||||
|
||||
@@ -23,7 +23,8 @@ export const useUserConfigStore = create<UserConfigState>(set => ({
|
||||
hide_watched: false,
|
||||
file_size_unit: 'binary',
|
||||
show_ignored_only: false,
|
||||
show_subed_only: false,
|
||||
show_subed_only: null,
|
||||
show_subed_only_playlists: null,
|
||||
show_help_text: true,
|
||||
},
|
||||
setUserConfig: userConfig => set({ userConfig }),
|
||||
|
||||
Reference in New Issue
Block a user