mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-05 01:19:29 +00:00
split between userConfig and userAccount
This commit is contained in:
@@ -1,17 +1,6 @@
|
||||
import { SortByType, SortOrderType, ViewLayoutType } from '../../pages/Home';
|
||||
import APIClient from '../../functions/APIClient';
|
||||
|
||||
export type UserMeType = {
|
||||
id: number;
|
||||
name: string;
|
||||
is_superuser: boolean;
|
||||
is_staff: boolean;
|
||||
groups: [];
|
||||
user_permissions: [];
|
||||
last_login: string;
|
||||
config: UserConfigType;
|
||||
};
|
||||
|
||||
export type ColourVariants = 'dark.css' | 'light.css' | 'matrix.css' | 'midnight.css';
|
||||
|
||||
export type UserConfigType = {
|
||||
@@ -33,7 +22,7 @@ export type UserConfigType = {
|
||||
const updateUserConfig = async (config: Partial<UserConfigType>): Promise<UserConfigType> => {
|
||||
return APIClient('/api/user/me/', {
|
||||
method: 'POST',
|
||||
body: { config: config },
|
||||
body: config,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
8
frontend/src/api/loader/loadUserAccount.ts
Normal file
8
frontend/src/api/loader/loadUserAccount.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import APIClient from '../../functions/APIClient';
|
||||
import { UserAccountType } from '../../pages/Base';
|
||||
|
||||
const loadUserAccount = async (): Promise<UserAccountType> => {
|
||||
return APIClient('/api/user/account/');
|
||||
};
|
||||
|
||||
export default loadUserAccount;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { UserMeType } from '../actions/updateUserConfig';
|
||||
import { UserConfigType } from '../actions/updateUserConfig';
|
||||
import APIClient from '../../functions/APIClient';
|
||||
|
||||
const loadUserMeConfig = async (): Promise<UserMeType> => {
|
||||
const loadUserMeConfig = async (): Promise<UserConfigType> => {
|
||||
return APIClient('/api/user/me/');
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ type ChannelListProps = {
|
||||
|
||||
const ChannelList = ({ channelList, refreshChannelList }: ChannelListProps) => {
|
||||
const { userConfig } = useUserConfigStore();
|
||||
const viewLayout = userConfig.config.view_style_channel;
|
||||
const viewLayout = userConfig.view_style_channel;
|
||||
|
||||
if (!channelList || channelList.length === 0) {
|
||||
return <p>No channels found.</p>;
|
||||
|
||||
@@ -16,8 +16,8 @@ type DownloadListItemProps = {
|
||||
|
||||
const DownloadListItem = ({ download, setRefresh }: DownloadListItemProps) => {
|
||||
const { userConfig } = useUserConfigStore();
|
||||
const view = userConfig.config.view_style_downloads;
|
||||
const showIgnored = userConfig.config.show_ignored_only;
|
||||
const view = userConfig.view_style_downloads;
|
||||
const showIgnored = userConfig.show_ignored_only;
|
||||
|
||||
const [hideDownload, setHideDownload] = useState(false);
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ const EmbeddableVideoPlayer = ({ videoId }: EmbeddableVideoPlayerProps) => {
|
||||
setRefresh(false);
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [videoId, refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import iconListView from '/img/icon-listview.svg';
|
||||
import { SortByType, SortOrderType } from '../pages/Home';
|
||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||
import { ViewStyles } from '../configuration/constants/ViewStyle';
|
||||
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||
|
||||
type FilterbarProps = {
|
||||
hideToggleText: string;
|
||||
@@ -15,9 +16,14 @@ type FilterbarProps = {
|
||||
};
|
||||
|
||||
const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: FilterbarProps) => {
|
||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
||||
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||
const [showHidden, setShowHidden] = useState(false);
|
||||
const isGridView = userConfig.config.view_style_home === ViewStyles.grid;
|
||||
const isGridView = userConfig.view_style_home === ViewStyles.grid;
|
||||
|
||||
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||
const updatedUserConfig = await updateUserConfig(config);
|
||||
setUserConfig(updatedUserConfig);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="view-controls three">
|
||||
@@ -27,13 +33,13 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
||||
<input
|
||||
id="hide_watched"
|
||||
type="checkbox"
|
||||
checked={userConfig.config.hide_watched}
|
||||
checked={userConfig.hide_watched}
|
||||
onChange={() => {
|
||||
setPartialConfig({ hide_watched: !userConfig.config.hide_watched });
|
||||
handleUserConfigUpdate({ hide_watched: !userConfig.hide_watched });
|
||||
}}
|
||||
/>
|
||||
|
||||
{userConfig.config.hide_watched ? (
|
||||
{userConfig.hide_watched ? (
|
||||
<label htmlFor="" className="onbtn">
|
||||
On
|
||||
</label>
|
||||
@@ -52,9 +58,9 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
||||
<select
|
||||
name="sort_by"
|
||||
id="sort"
|
||||
value={userConfig.config.sort_by}
|
||||
value={userConfig.sort_by}
|
||||
onChange={event => {
|
||||
setPartialConfig({ sort_by: event.target.value as SortByType });
|
||||
handleUserConfigUpdate({ sort_by: event.target.value as SortByType });
|
||||
}}
|
||||
>
|
||||
<option value="published">date published</option>
|
||||
@@ -67,9 +73,9 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
||||
<select
|
||||
name="sort_order"
|
||||
id="sort-order"
|
||||
value={userConfig.config.sort_order}
|
||||
value={userConfig.sort_order}
|
||||
onChange={event => {
|
||||
setPartialConfig({ sort_order: event.target.value as SortOrderType });
|
||||
handleUserConfigUpdate({ sort_order: event.target.value as SortOrderType });
|
||||
}}
|
||||
>
|
||||
<option value="asc">asc</option>
|
||||
@@ -90,22 +96,22 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
||||
/>
|
||||
)}
|
||||
|
||||
{userConfig.config.grid_items !== undefined && isGridView && (
|
||||
{userConfig.grid_items !== undefined && isGridView && (
|
||||
<div className="grid-count">
|
||||
{userConfig.config.grid_items < 7 && (
|
||||
{userConfig.grid_items < 7 && (
|
||||
<img
|
||||
src={iconAdd}
|
||||
onClick={() => {
|
||||
setPartialConfig({ grid_items: userConfig.config.grid_items + 1 });
|
||||
handleUserConfigUpdate({ grid_items: userConfig.grid_items + 1 });
|
||||
}}
|
||||
alt="grid plus row"
|
||||
/>
|
||||
)}
|
||||
{userConfig.config.grid_items > 3 && (
|
||||
{userConfig.grid_items > 3 && (
|
||||
<img
|
||||
src={iconSubstract}
|
||||
onClick={() => {
|
||||
setPartialConfig({ grid_items: userConfig.config.grid_items - 1 });
|
||||
handleUserConfigUpdate({ grid_items: userConfig.grid_items - 1 });
|
||||
}}
|
||||
alt="grid minus row"
|
||||
/>
|
||||
@@ -115,14 +121,14 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
||||
<img
|
||||
src={iconGridView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ [viewStyleName]: 'grid' });
|
||||
handleUserConfigUpdate({ [viewStyleName]: 'grid' });
|
||||
}}
|
||||
alt="grid view"
|
||||
/>
|
||||
<img
|
||||
src={iconListView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ [viewStyleName]: 'list' });
|
||||
handleUserConfigUpdate({ [viewStyleName]: 'list' });
|
||||
}}
|
||||
alt="list view"
|
||||
/>
|
||||
|
||||
@@ -93,8 +93,10 @@ const GoogleCast = ({ video, setRefresh, onWatchStateChanged }: GoogleCastProps)
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
|
||||
const setup = useCallback(() => {
|
||||
const cast = globalThis.cast;
|
||||
const chrome = globalThis.chrome;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const cast = (globalThis as any).cast;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const chrome = (globalThis as any).chrome;
|
||||
|
||||
cast.framework.CastContext.getInstance().setOptions({
|
||||
receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID, // Use built in receiver app on cast device, see https://developers.google.com/cast/docs/styled_receiver if you want to be able to add a theme, splash screen or watermark. Has a $5 one time fee.
|
||||
@@ -132,8 +134,10 @@ const GoogleCast = ({ video, setRefresh, onWatchStateChanged }: GoogleCastProps)
|
||||
}, [setRefresh, video]);
|
||||
|
||||
const startPlayback = useCallback(() => {
|
||||
const chrome = globalThis.chrome;
|
||||
const cast = globalThis.cast;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const chrome = (globalThis as any).chrome;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const cast = (globalThis as any).cast;
|
||||
const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
|
||||
|
||||
const mediaUrl = video?.media_url;
|
||||
|
||||
@@ -14,7 +14,7 @@ type PlaylistListProps = {
|
||||
|
||||
const PlaylistList = ({ playlistList, setRefresh }: PlaylistListProps) => {
|
||||
const { userConfig } = useUserConfigStore();
|
||||
const viewLayout = userConfig.config.view_style_playlist;
|
||||
const viewLayout = userConfig.view_style_playlist;
|
||||
|
||||
if (!playlistList || playlistList.length === 0) {
|
||||
return <p>No playlists found.</p>;
|
||||
|
||||
@@ -59,7 +59,6 @@ const Subtitles = ({ subtitles }: SubtitlesProp) => {
|
||||
const handleTimeUpdate =
|
||||
(
|
||||
youtubeId: string,
|
||||
duration: number,
|
||||
watched: boolean,
|
||||
sponsorBlock?: SponsorBlockType,
|
||||
setSponsorSegmentSkipped?: Dispatch<SetStateAction<SponsorSegmentsSkippedType>>,
|
||||
@@ -337,7 +336,6 @@ const VideoPlayer = ({
|
||||
}}
|
||||
onTimeUpdate={handleTimeUpdate(
|
||||
videoId,
|
||||
duration,
|
||||
watched,
|
||||
sponsorBlock,
|
||||
setSkippedSegments,
|
||||
|
||||
@@ -9,7 +9,7 @@ export const ColourConstant = {
|
||||
|
||||
const useColours = () => {
|
||||
const { userConfig } = useUserConfigStore();
|
||||
const stylesheet = userConfig?.config.stylesheet;
|
||||
const stylesheet = userConfig?.stylesheet;
|
||||
|
||||
switch (stylesheet) {
|
||||
case ColourConstant.Dark:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||
import { useUserAccountStore } from '../stores/UserAccountStore';
|
||||
|
||||
const useIsAdmin = () => {
|
||||
const { userConfig } = useUserConfigStore();
|
||||
const isAdmin = userConfig?.is_staff || userConfig?.is_superuser;
|
||||
const { userAccount } = useUserAccountStore();
|
||||
const isAdmin = userAccount?.is_staff || userAccount?.is_superuser;
|
||||
|
||||
return isAdmin;
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ import ChannelVideo from './pages/ChannelVideo';
|
||||
import ChannelPlaylist from './pages/ChannelPlaylist';
|
||||
import ChannelAbout from './pages/ChannelAbout';
|
||||
import Download from './pages/Download';
|
||||
import loadUserAccount from './api/loader/loadUserAccount';
|
||||
|
||||
const router = createBrowserRouter(
|
||||
[
|
||||
@@ -41,8 +42,9 @@ const router = createBrowserRouter(
|
||||
const authData = await auth.json();
|
||||
|
||||
const userConfig = await loadUserMeConfig();
|
||||
const userAccount = await loadUserAccount();
|
||||
|
||||
return { userConfig, auth: authData };
|
||||
return { userConfig, userAccount, auth: authData };
|
||||
},
|
||||
element: <Base />,
|
||||
errorElement: <ErrorPage />,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Outlet, useLoaderData, useLocation, useSearchParams } from 'react-router-dom';
|
||||
import Footer from '../components/Footer';
|
||||
import useColours from '../configuration/colours/useColours';
|
||||
import { UserMeType } from '../api/actions/updateUserConfig';
|
||||
import { UserConfigType } from '../api/actions/updateUserConfig';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Navigation from '../components/Navigation';
|
||||
import { useAuthStore } from '../stores/AuthDataStore';
|
||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||
import { useUserAccountStore } from '../stores/UserAccountStore';
|
||||
|
||||
export type TaUpdateType = {
|
||||
version?: string;
|
||||
@@ -19,8 +20,19 @@ export type AuthenticationType = {
|
||||
ta_update: TaUpdateType;
|
||||
};
|
||||
|
||||
export type UserAccountType = {
|
||||
id: number;
|
||||
name: string;
|
||||
is_superuser: boolean;
|
||||
is_staff: boolean;
|
||||
groups: [];
|
||||
user_permissions: [];
|
||||
last_login: string;
|
||||
};
|
||||
|
||||
type BaseLoaderData = {
|
||||
userConfig: UserMeType;
|
||||
userConfig: UserConfigType;
|
||||
userAccount: UserAccountType;
|
||||
auth: AuthenticationType;
|
||||
};
|
||||
|
||||
@@ -32,7 +44,8 @@ export type OutletContextType = {
|
||||
const Base = () => {
|
||||
const { setAuth } = useAuthStore();
|
||||
const { setUserConfig } = useUserConfigStore();
|
||||
const { userConfig, auth } = useLoaderData() as BaseLoaderData;
|
||||
const { setUserAccount } = useUserAccountStore();
|
||||
const { userConfig, userAccount, auth } = useLoaderData() as BaseLoaderData;
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
@@ -46,6 +59,7 @@ const Base = () => {
|
||||
useEffect(() => {
|
||||
setAuth(auth);
|
||||
setUserConfig(userConfig);
|
||||
setUserAccount(userAccount);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ const ChannelAbout = () => {
|
||||
<div className="info-box">
|
||||
<div className="info-box-item">
|
||||
<h2>Channel Customization</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<ul>
|
||||
<li>Overwrite the download format over the format set globally.</li>
|
||||
@@ -334,7 +334,7 @@ const ChannelAbout = () => {
|
||||
</div>
|
||||
<div className="info-box-item">
|
||||
<h2>Page Size Overrides</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<p>
|
||||
Disable standard videos, shorts, or streams for this channel by setting their
|
||||
|
||||
@@ -10,10 +10,11 @@ import { PlaylistsResponseType } from './Playlists';
|
||||
import iconGridView from '/img/icon-gridview.svg';
|
||||
import iconListView from '/img/icon-listview.svg';
|
||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||
|
||||
const ChannelPlaylist = () => {
|
||||
const { channelId } = useParams();
|
||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
||||
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
||||
|
||||
const [refreshPlaylists, setRefreshPlaylists] = useState(false);
|
||||
@@ -23,8 +24,13 @@ const ChannelPlaylist = () => {
|
||||
const playlistList = playlistsResponse?.data;
|
||||
const pagination = playlistsResponse?.paginate;
|
||||
|
||||
const view = userConfig.config.view_style_playlist;
|
||||
const showSubedOnly = userConfig.config.show_subed_only;
|
||||
const view = userConfig.view_style_playlist;
|
||||
const showSubedOnly = userConfig.show_subed_only;
|
||||
|
||||
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||
const updatedUserConfig = await updateUserConfig(config);
|
||||
setUserConfig(updatedUserConfig);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -52,7 +58,7 @@ const ChannelPlaylist = () => {
|
||||
<input
|
||||
checked={showSubedOnly}
|
||||
onChange={() => {
|
||||
setPartialConfig({ show_subed_only: !showSubedOnly });
|
||||
handleUserConfigUpdate({ show_subed_only: !showSubedOnly });
|
||||
setRefreshPlaylists(true);
|
||||
}}
|
||||
type="checkbox"
|
||||
@@ -73,14 +79,14 @@ const ChannelPlaylist = () => {
|
||||
<img
|
||||
src={iconGridView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ view_style_playlist: 'grid' });
|
||||
handleUserConfigUpdate({ view_style_playlist: 'grid' });
|
||||
}}
|
||||
alt="grid view"
|
||||
/>
|
||||
<img
|
||||
src={iconListView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ view_style_playlist: 'list' });
|
||||
handleUserConfigUpdate({ view_style_playlist: 'list' });
|
||||
}}
|
||||
alt="list view"
|
||||
/>
|
||||
|
||||
@@ -49,10 +49,10 @@ const ChannelVideo = ({ videoType }: ChannelVideoProps) => {
|
||||
const hasVideos = videoResponse?.data?.length !== 0;
|
||||
const showEmbeddedVideo = videoId !== null;
|
||||
|
||||
const view = userConfig.config.view_style_home;
|
||||
const view = userConfig.view_style_home;
|
||||
const isGridView = view === ViewStyles.grid;
|
||||
const gridView = isGridView ? `boxed-${userConfig.config.grid_items}` : '';
|
||||
const gridViewGrid = isGridView ? `grid-${userConfig.config.grid_items}` : '';
|
||||
const gridView = isGridView ? `boxed-${userConfig.grid_items}` : '';
|
||||
const gridViewGrid = isGridView ? `grid-${userConfig.grid_items}` : '';
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -60,9 +60,9 @@ const ChannelVideo = ({ videoType }: ChannelVideoProps) => {
|
||||
const videos = await loadVideoListByFilter({
|
||||
channel: channelId,
|
||||
page: currentPage,
|
||||
watch: userConfig.config.hide_watched ? 'unwatched' : undefined,
|
||||
sort: userConfig.config.sort_by,
|
||||
order: userConfig.config.sort_order,
|
||||
watch: userConfig.hide_watched ? 'unwatched' : undefined,
|
||||
sort: userConfig.sort_by,
|
||||
order: userConfig.sort_order,
|
||||
type: videoType,
|
||||
});
|
||||
const channelAggs = await loadChannelAggs(channelId);
|
||||
@@ -74,9 +74,9 @@ const ChannelVideo = ({ videoType }: ChannelVideoProps) => {
|
||||
})();
|
||||
}, [
|
||||
refresh,
|
||||
userConfig.config.sort_by,
|
||||
userConfig.config.sort_order,
|
||||
userConfig.config.hide_watched,
|
||||
userConfig.sort_by,
|
||||
userConfig.sort_order,
|
||||
userConfig.hide_watched,
|
||||
currentPage,
|
||||
channelId,
|
||||
pagination?.current_page,
|
||||
|
||||
@@ -14,6 +14,7 @@ import Button from '../components/Button';
|
||||
import updateBulkChannelSubscriptions from '../api/actions/updateBulkChannelSubscriptions';
|
||||
import useIsAdmin from '../functions/useIsAdmin';
|
||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||
|
||||
type ChannelOverwritesType = {
|
||||
download_format?: string;
|
||||
@@ -48,7 +49,7 @@ type ChannelsListResponse = {
|
||||
};
|
||||
|
||||
const Channels = () => {
|
||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
||||
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
||||
const isAdmin = useIsAdmin();
|
||||
|
||||
@@ -63,18 +64,20 @@ const Channels = () => {
|
||||
// const channelCount = pagination?.total_hits;
|
||||
const hasChannels = channels?.length !== 0;
|
||||
|
||||
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||
const updatedUserConfig = await updateUserConfig(config);
|
||||
setUserConfig(updatedUserConfig);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const channelListResponse = await loadChannelList(
|
||||
currentPage,
|
||||
userConfig.config.show_subed_only,
|
||||
);
|
||||
const channelListResponse = await loadChannelList(currentPage, userConfig.show_subed_only);
|
||||
|
||||
setChannelListResponse(channelListResponse);
|
||||
setShowNotification(false);
|
||||
setRefresh(false);
|
||||
})();
|
||||
}, [refresh, userConfig.config.show_subed_only, currentPage, pagination?.current_page]);
|
||||
}, [refresh, userConfig.show_subed_only, currentPage, pagination?.current_page]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -145,18 +148,18 @@ const Channels = () => {
|
||||
<input
|
||||
id="show_subed_only"
|
||||
onChange={async () => {
|
||||
setPartialConfig({ show_subed_only: !userConfig.config.show_subed_only });
|
||||
handleUserConfigUpdate({ show_subed_only: !userConfig.show_subed_only });
|
||||
setRefresh(true);
|
||||
}}
|
||||
type="checkbox"
|
||||
checked={userConfig.config.show_subed_only}
|
||||
checked={userConfig.show_subed_only}
|
||||
/>
|
||||
{!userConfig.config.show_subed_only && (
|
||||
{!userConfig.show_subed_only && (
|
||||
<label htmlFor="" className="ofbtn">
|
||||
Off
|
||||
</label>
|
||||
)}
|
||||
{userConfig.config.show_subed_only && (
|
||||
{userConfig.show_subed_only && (
|
||||
<label htmlFor="" className="onbtn">
|
||||
On
|
||||
</label>
|
||||
@@ -167,7 +170,7 @@ const Channels = () => {
|
||||
<img
|
||||
src={iconGridView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ view_style_channel: 'grid' });
|
||||
handleUserConfigUpdate({ view_style_channel: 'grid' });
|
||||
}}
|
||||
data-origin="channel"
|
||||
data-value="grid"
|
||||
@@ -176,7 +179,7 @@ const Channels = () => {
|
||||
<img
|
||||
src={iconListView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ view_style_channel: 'list' });
|
||||
handleUserConfigUpdate({ view_style_channel: 'list' });
|
||||
}}
|
||||
data-origin="channel"
|
||||
data-value="list"
|
||||
@@ -186,7 +189,7 @@ const Channels = () => {
|
||||
</div>
|
||||
{/* {hasChannels && <h2>Total channels: {channelCount}</h2>} */}
|
||||
|
||||
<div className={`channel-list ${userConfig.config.view_style_channel}`}>
|
||||
<div className={`channel-list ${userConfig.view_style_channel}`}>
|
||||
{!hasChannels && <h2>No channels found...</h2>}
|
||||
|
||||
{hasChannels && <ChannelList channelList={channels} refreshChannelList={setRefresh} />}
|
||||
|
||||
@@ -19,6 +19,7 @@ import Button from '../components/Button';
|
||||
import DownloadListItem from '../components/DownloadListItem';
|
||||
import loadDownloadAggs, { DownloadAggsType } from '../api/loader/loadDownloadAggs';
|
||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||
|
||||
type Download = {
|
||||
auto_start: boolean;
|
||||
@@ -46,7 +47,7 @@ export type DownloadResponseType = {
|
||||
|
||||
const Download = () => {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
||||
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
||||
|
||||
const channelFilterFromUrl = searchParams.get('channel');
|
||||
@@ -75,13 +76,18 @@ const Download = () => {
|
||||
? downloadResponse?.data[0].channel_name
|
||||
: '';
|
||||
|
||||
const view = userConfig.config.view_style_downloads;
|
||||
const gridItems = userConfig.config.grid_items;
|
||||
const showIgnored = userConfig.config.show_ignored_only;
|
||||
const view = userConfig.view_style_downloads;
|
||||
const gridItems = userConfig.grid_items;
|
||||
const showIgnored = userConfig.show_ignored_only;
|
||||
const isGridView = view === ViewStyles.grid;
|
||||
const gridView = isGridView ? `boxed-${gridItems}` : '';
|
||||
const gridViewGrid = isGridView ? `grid-${gridItems}` : '';
|
||||
|
||||
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||
const updatedUserConfig = await updateUserConfig(config);
|
||||
setUserConfig(updatedUserConfig);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const videos = await loadDownloadQueue(currentPage, channelFilterFromUrl, showIgnored);
|
||||
@@ -208,7 +214,7 @@ const Download = () => {
|
||||
<input
|
||||
id="showIgnored"
|
||||
onChange={() => {
|
||||
setPartialConfig({ show_ignored_only: !showIgnored });
|
||||
handleUserConfigUpdate({ show_ignored_only: !showIgnored });
|
||||
setRefresh(true);
|
||||
}}
|
||||
type="checkbox"
|
||||
@@ -263,7 +269,7 @@ const Download = () => {
|
||||
<img
|
||||
src={iconAdd}
|
||||
onClick={() => {
|
||||
setPartialConfig({ grid_items: gridItems + 1 });
|
||||
handleUserConfigUpdate({ grid_items: gridItems + 1 });
|
||||
}}
|
||||
alt="grid plus row"
|
||||
/>
|
||||
@@ -272,7 +278,7 @@ const Download = () => {
|
||||
<img
|
||||
src={iconSubstract}
|
||||
onClick={() => {
|
||||
setPartialConfig({ grid_items: gridItems - 1 });
|
||||
handleUserConfigUpdate({ grid_items: gridItems - 1 });
|
||||
}}
|
||||
alt="grid minus row"
|
||||
/>
|
||||
@@ -283,14 +289,14 @@ const Download = () => {
|
||||
<img
|
||||
src={iconGridView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ view_style_downloads: 'grid' });
|
||||
handleUserConfigUpdate({ view_style_downloads: 'grid' });
|
||||
}}
|
||||
alt="grid view"
|
||||
/>
|
||||
<img
|
||||
src={iconListView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ view_style_downloads: 'list' });
|
||||
handleUserConfigUpdate({ view_style_downloads: 'list' });
|
||||
}}
|
||||
alt="list view"
|
||||
/>
|
||||
|
||||
@@ -108,8 +108,6 @@ const Home = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const videoId = searchParams.get('videoId');
|
||||
|
||||
const userMeConfig = userConfig.config;
|
||||
|
||||
const [refreshVideoList, setRefreshVideoList] = useState(false);
|
||||
|
||||
const [videoResponse, setVideoReponse] = useState<VideoListByFilterResponseType>();
|
||||
@@ -123,17 +121,17 @@ const Home = () => {
|
||||
const hasVideos = videoResponse?.data?.length !== 0;
|
||||
const showEmbeddedVideo = videoId !== null;
|
||||
|
||||
const isGridView = userMeConfig.view_style_home === ViewStyles.grid;
|
||||
const gridView = isGridView ? `boxed-${userMeConfig.grid_items}` : '';
|
||||
const gridViewGrid = isGridView ? `grid-${userMeConfig.grid_items}` : '';
|
||||
const isGridView = userConfig.view_style_home === ViewStyles.grid;
|
||||
const gridView = isGridView ? `boxed-${userConfig.grid_items}` : '';
|
||||
const gridViewGrid = isGridView ? `grid-${userConfig.grid_items}` : '';
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const videos = await loadVideoListByFilter({
|
||||
page: currentPage,
|
||||
watch: userMeConfig.hide_watched ? 'unwatched' : undefined,
|
||||
sort: userMeConfig.sort_by,
|
||||
order: userMeConfig.sort_order,
|
||||
watch: userConfig.hide_watched ? 'unwatched' : undefined,
|
||||
sort: userConfig.sort_by,
|
||||
order: userConfig.sort_order,
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -150,9 +148,9 @@ const Home = () => {
|
||||
})();
|
||||
}, [
|
||||
refreshVideoList,
|
||||
userMeConfig.sort_by,
|
||||
userMeConfig.sort_order,
|
||||
userMeConfig.hide_watched,
|
||||
userConfig.sort_by,
|
||||
userConfig.sort_order,
|
||||
userConfig.hide_watched,
|
||||
currentPage,
|
||||
pagination?.current_page,
|
||||
showEmbeddedVideo,
|
||||
@@ -171,10 +169,10 @@ const Home = () => {
|
||||
<div className="title-bar">
|
||||
<h1>Continue Watching</h1>
|
||||
</div>
|
||||
<div className={`video-list ${userMeConfig.view_style_home} ${gridViewGrid}`}>
|
||||
<div className={`video-list ${userConfig.view_style_home} ${gridViewGrid}`}>
|
||||
<VideoList
|
||||
videoList={continueVideos}
|
||||
viewLayout={userMeConfig.view_style_home}
|
||||
viewLayout={userConfig.view_style_home}
|
||||
refreshVideoList={setRefreshVideoList}
|
||||
/>
|
||||
</div>
|
||||
@@ -189,7 +187,7 @@ const Home = () => {
|
||||
</div>
|
||||
|
||||
<div className={`boxed-content ${gridView}`}>
|
||||
<div className={`video-list ${userMeConfig.view_style_home} ${gridViewGrid}`}>
|
||||
<div className={`video-list ${userConfig.view_style_home} ${gridViewGrid}`}>
|
||||
{!hasVideos && (
|
||||
<>
|
||||
<h2>No videos found...</h2>
|
||||
@@ -204,7 +202,7 @@ const Home = () => {
|
||||
{hasVideos && (
|
||||
<VideoList
|
||||
videoList={videoList}
|
||||
viewLayout={userMeConfig.view_style_home}
|
||||
viewLayout={userConfig.view_style_home}
|
||||
refreshVideoList={setRefreshVideoList}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -81,9 +81,9 @@ const Playlist = () => {
|
||||
const videoInPlaylistCount = pagination?.total_hits;
|
||||
const showEmbeddedVideo = videoId !== null;
|
||||
|
||||
const view = userConfig.config.view_style_home;
|
||||
const gridItems = userConfig.config.grid_items;
|
||||
const hideWatched = userConfig.config.hide_watched;
|
||||
const view = userConfig.view_style_home;
|
||||
const gridItems = userConfig.grid_items;
|
||||
const hideWatched = userConfig.hide_watched;
|
||||
const isGridView = view === ViewStyles.grid;
|
||||
const gridView = isGridView ? `boxed-${gridItems}` : '';
|
||||
const gridViewGrid = isGridView ? `grid-${gridItems}` : '';
|
||||
@@ -112,7 +112,7 @@ const Playlist = () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
playlistId,
|
||||
userConfig.config.hide_watched,
|
||||
userConfig.hide_watched,
|
||||
refresh,
|
||||
currentPage,
|
||||
pagination?.current_page,
|
||||
|
||||
@@ -18,6 +18,7 @@ import Button from '../components/Button';
|
||||
import useIsAdmin from '../functions/useIsAdmin';
|
||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||
import Notifications from '../components/Notifications';
|
||||
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||
|
||||
export type PlaylistEntryType = {
|
||||
youtube_id: string;
|
||||
@@ -34,7 +35,7 @@ export type PlaylistsResponseType = {
|
||||
};
|
||||
|
||||
const Playlists = () => {
|
||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
||||
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
||||
const isAdmin = useIsAdmin();
|
||||
|
||||
@@ -51,8 +52,8 @@ const Playlists = () => {
|
||||
|
||||
const hasPlaylists = playlistResponse?.data?.length !== 0;
|
||||
|
||||
const view = userConfig.config.view_style_playlist;
|
||||
const showSubedOnly = userConfig.config.show_subed_only;
|
||||
const view = userConfig.view_style_playlist;
|
||||
const showSubedOnly = userConfig.show_subed_only;
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -66,7 +67,12 @@ const Playlists = () => {
|
||||
setShowNotification(false);
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [refresh, userConfig.config.show_subed_only, currentPage, pagination?.current_page]);
|
||||
}, [refresh, userConfig.show_subed_only, currentPage, pagination?.current_page]);
|
||||
|
||||
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||
const updatedUserConfig = await updateUserConfig(config);
|
||||
setUserConfig(updatedUserConfig);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -156,7 +162,7 @@ const Playlists = () => {
|
||||
<input
|
||||
checked={showSubedOnly}
|
||||
onChange={() => {
|
||||
setPartialConfig({ show_subed_only: !showSubedOnly });
|
||||
handleUserConfigUpdate({ show_subed_only: !showSubedOnly });
|
||||
}}
|
||||
type="checkbox"
|
||||
/>
|
||||
@@ -176,14 +182,14 @@ const Playlists = () => {
|
||||
<img
|
||||
src={iconGridView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ view_style_playlist: 'grid' });
|
||||
handleUserConfigUpdate({ view_style_playlist: 'grid' });
|
||||
}}
|
||||
alt="grid view"
|
||||
/>
|
||||
<img
|
||||
src={iconListView}
|
||||
onClick={() => {
|
||||
setPartialConfig({ view_style_playlist: 'list' });
|
||||
handleUserConfigUpdate({ view_style_playlist: 'list' });
|
||||
}}
|
||||
alt="list view"
|
||||
/>
|
||||
|
||||
@@ -39,12 +39,11 @@ const Search = () => {
|
||||
const { userConfig } = useUserConfigStore();
|
||||
const [searchParams] = useSearchParams();
|
||||
const videoId = searchParams.get('videoId');
|
||||
const userMeConfig = userConfig.config;
|
||||
|
||||
const viewVideos = userMeConfig.view_style_home;
|
||||
const viewChannels = userMeConfig.view_style_channel;
|
||||
const viewPlaylists = userMeConfig.view_style_playlist;
|
||||
const gridItems = userMeConfig.grid_items || 3;
|
||||
const viewVideos = userConfig.view_style_home;
|
||||
const viewChannels = userConfig.view_style_channel;
|
||||
const viewPlaylists = userConfig.view_style_playlist;
|
||||
const gridItems = userConfig.grid_items || 3;
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState<string>('');
|
||||
|
||||
@@ -209,7 +209,7 @@ const SettingsApplication = () => {
|
||||
<div className="info-box">
|
||||
<div className="info-box-item">
|
||||
<h2 id="subscriptions">Subscription Scan</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<p>Configure how a subscription Scan tracks videos.</p>
|
||||
<ul>
|
||||
@@ -275,7 +275,7 @@ const SettingsApplication = () => {
|
||||
</div>
|
||||
<div className="info-box-item">
|
||||
<h2 id="downloads">Downloads</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<ul>
|
||||
<li>
|
||||
@@ -362,7 +362,7 @@ const SettingsApplication = () => {
|
||||
</div>
|
||||
<div className="info-box-item">
|
||||
<h2 id="format">Download Format</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<ul>
|
||||
<li>
|
||||
@@ -507,7 +507,7 @@ const SettingsApplication = () => {
|
||||
</div>
|
||||
<div className="info-box-item">
|
||||
<h2 id="subtitles">Subtitles</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<p>Additional subtitle options show once you choose a language.</p>
|
||||
<ul>
|
||||
@@ -586,7 +586,7 @@ const SettingsApplication = () => {
|
||||
</div>
|
||||
<div className="info-box-item">
|
||||
<h2 id="comments">Comments</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<p>Additional options show once you set a comment index option.</p>
|
||||
<ul>
|
||||
@@ -650,7 +650,7 @@ const SettingsApplication = () => {
|
||||
</div>
|
||||
<div className="info-box-item">
|
||||
<h2 id="cookie">Cookie</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<p>
|
||||
Importing your cookie will authenticate requests to YT with your user account.
|
||||
@@ -777,7 +777,7 @@ const SettingsApplication = () => {
|
||||
</div>
|
||||
<div className="info-box-item">
|
||||
<h2 id="sntegrations">Integrations</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<ul>
|
||||
<li>The API token is used to make automater requests to TA through the API.</li>
|
||||
@@ -868,7 +868,7 @@ const SettingsApplication = () => {
|
||||
</div>
|
||||
<div className="info-box-item">
|
||||
<h2>Snapshots</h2>
|
||||
{userConfig.config.show_help_text && (
|
||||
{userConfig.show_help_text && (
|
||||
<div className="help-text">
|
||||
<p>
|
||||
Automatically create daily deduplicated snapshots of the index, stored in
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ColourVariants } from '../api/actions/updateUserConfig';
|
||||
import updateUserConfig, { ColourVariants, UserConfigType } from '../api/actions/updateUserConfig';
|
||||
import { ColourConstant } from '../configuration/colours/useColours';
|
||||
import SettingsNavigation from '../components/SettingsNavigation';
|
||||
import Notifications from '../components/Notifications';
|
||||
@@ -10,35 +10,40 @@ import { useEffect, useState } from 'react';
|
||||
import ToggleConfig from '../components/ToggleConfig';
|
||||
|
||||
const SettingsUser = () => {
|
||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
||||
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||
const isAdmin = useIsAdmin();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [styleSheet, setStyleSheet] = useState<ColourVariants>(userConfig.config.stylesheet);
|
||||
const [styleSheet, setStyleSheet] = useState<ColourVariants>(userConfig.stylesheet);
|
||||
const [styleSheetRefresh, setStyleSheetRefresh] = useState(false);
|
||||
const [pageSize, setPageSize] = useState<number>(userConfig.config.page_size);
|
||||
const [showHelpText, setShowHelpText] = useState(userConfig.config.show_help_text);
|
||||
const [pageSize, setPageSize] = useState<number>(userConfig.page_size);
|
||||
const [showHelpText, setShowHelpText] = useState(userConfig.show_help_text);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setStyleSheet(userConfig.config.stylesheet);
|
||||
setPageSize(userConfig.config.page_size);
|
||||
setShowHelpText(userConfig.config.show_help_text);
|
||||
setStyleSheet(userConfig.stylesheet);
|
||||
setPageSize(userConfig.page_size);
|
||||
setShowHelpText(userConfig.show_help_text);
|
||||
})();
|
||||
}, [userConfig.config.page_size, userConfig.config.stylesheet, userConfig.config.show_help_text]);
|
||||
}, [userConfig.page_size, userConfig.stylesheet, userConfig.show_help_text]);
|
||||
|
||||
const handleStyleSheetChange = async (selectedStyleSheet: ColourVariants) => {
|
||||
setPartialConfig({ stylesheet: selectedStyleSheet });
|
||||
handleUserConfigUpdate({ stylesheet: selectedStyleSheet });
|
||||
setStyleSheet(selectedStyleSheet);
|
||||
setStyleSheetRefresh(true);
|
||||
};
|
||||
|
||||
const handlePageSizeChange = async () => {
|
||||
setPartialConfig({ page_size: pageSize });
|
||||
handleUserConfigUpdate({ page_size: pageSize });
|
||||
};
|
||||
|
||||
const handleShowHelpTextChange = async (configKey: string, configValue: boolean) => {
|
||||
setPartialConfig({ [configKey]: configValue });
|
||||
handleUserConfigUpdate({ [configKey]: configValue });
|
||||
};
|
||||
|
||||
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||
const updatedUserConfig = await updateUserConfig(config);
|
||||
setUserConfig(updatedUserConfig);
|
||||
};
|
||||
|
||||
const handlePageRefresh = () => {
|
||||
@@ -99,12 +104,10 @@ const SettingsUser = () => {
|
||||
/>
|
||||
|
||||
<div className="button-box">
|
||||
{userConfig.config.page_size !== pageSize && (
|
||||
{userConfig.page_size !== pageSize && (
|
||||
<>
|
||||
<button onClick={handlePageSizeChange}>Update</button>
|
||||
<button onClick={() => setPageSize(userConfig.config.page_size)}>
|
||||
Cancel
|
||||
</button>
|
||||
<button onClick={() => setPageSize(userConfig.page_size)}>Cancel</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
12
frontend/src/stores/UserAccountStore.ts
Normal file
12
frontend/src/stores/UserAccountStore.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { create } from 'zustand';
|
||||
import { UserAccountType } from '../pages/Base';
|
||||
|
||||
interface AccountState {
|
||||
userAccount: UserAccountType | null;
|
||||
setUserAccount: (userAccount: UserAccountType) => void;
|
||||
}
|
||||
|
||||
export const useUserAccountStore = create<AccountState>(set => ({
|
||||
userAccount: null,
|
||||
setUserAccount: userAccount => set({ userAccount }),
|
||||
}));
|
||||
@@ -1,45 +1,26 @@
|
||||
import { create } from 'zustand';
|
||||
import updateUserConfig, { UserMeType, UserConfigType } from '../api/actions/updateUserConfig';
|
||||
import { UserConfigType } from '../api/actions/updateUserConfig';
|
||||
|
||||
interface UserConfigState {
|
||||
userConfig: UserMeType;
|
||||
setUserConfig: (userConfig: UserMeType) => void;
|
||||
setPartialConfig: (userConfig: Partial<UserConfigType>) => void;
|
||||
userConfig: UserConfigType;
|
||||
setUserConfig: (userConfig: UserConfigType) => void;
|
||||
}
|
||||
|
||||
export const useUserConfigStore = create<UserConfigState>(set => ({
|
||||
userConfig: {
|
||||
id: 0,
|
||||
name: '',
|
||||
is_superuser: false,
|
||||
is_staff: false,
|
||||
groups: [],
|
||||
user_permissions: [],
|
||||
last_login: '',
|
||||
config: {
|
||||
stylesheet: 'dark.css',
|
||||
page_size: 12,
|
||||
sort_by: 'published',
|
||||
sort_order: 'desc',
|
||||
view_style_home: 'grid',
|
||||
view_style_channel: 'list',
|
||||
view_style_downloads: 'list',
|
||||
view_style_playlist: 'grid',
|
||||
grid_items: 3,
|
||||
hide_watched: false,
|
||||
show_ignored_only: false,
|
||||
show_subed_only: false,
|
||||
show_help_text: true,
|
||||
},
|
||||
stylesheet: 'dark.css',
|
||||
page_size: 12,
|
||||
sort_by: 'published',
|
||||
sort_order: 'desc',
|
||||
view_style_home: 'grid',
|
||||
view_style_channel: 'list',
|
||||
view_style_downloads: 'list',
|
||||
view_style_playlist: 'grid',
|
||||
grid_items: 3,
|
||||
hide_watched: false,
|
||||
show_ignored_only: false,
|
||||
show_subed_only: false,
|
||||
show_help_text: true,
|
||||
},
|
||||
setUserConfig: userConfig => set({ userConfig }),
|
||||
|
||||
setPartialConfig: async (userConfig: Partial<UserConfigType>) => {
|
||||
const userConfigResponse = await updateUserConfig(userConfig);
|
||||
set(state => ({
|
||||
userConfig: state.userConfig
|
||||
? { ...state.userConfig, config: userConfigResponse }
|
||||
: state.userConfig,
|
||||
}));
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user