From 1177aa56997a4fac4ae82586579974beff035871 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 3 Jan 2025 19:05:25 +0700 Subject: [PATCH] refac channel about page settings, #852 --- .../src/api/actions/updateChannelOverwrite.ts | 33 ++ .../src/api/actions/updateChannelSettings.ts | 39 -- frontend/src/pages/ChannelAbout.tsx | 435 +++++++++--------- frontend/src/style.css | 36 +- 4 files changed, 263 insertions(+), 280 deletions(-) create mode 100644 frontend/src/api/actions/updateChannelOverwrite.ts delete mode 100644 frontend/src/api/actions/updateChannelSettings.ts diff --git a/frontend/src/api/actions/updateChannelOverwrite.ts b/frontend/src/api/actions/updateChannelOverwrite.ts new file mode 100644 index 00000000..2e76b442 --- /dev/null +++ b/frontend/src/api/actions/updateChannelOverwrite.ts @@ -0,0 +1,33 @@ +import defaultHeaders from '../../configuration/defaultHeaders'; +import getApiUrl from "../../configuration/getApiUrl" +import getFetchCredentials from '../../configuration/getFetchCredentials'; +import getCookie from "../../functions/getCookie"; + +const updateChannelOverwrites = async (channelId: string, configKey: string, configValue: any) => { + const apiUrl = getApiUrl(); + const csrfCookie = getCookie('csrftoken'); + + const data = { + channel_overwrites: { + [configKey]: configValue + } + }; + + const response = await fetch(`${apiUrl}/api/channel/${channelId}/`, { + method: 'POST', + headers: { + ...defaultHeaders, + 'X-CSRFToken': csrfCookie || '', + }, + credentials: getFetchCredentials(), + + body: JSON.stringify(data), + }); + + const channelSubscription = await response.json(); + console.log('updateChannelOverwrites', channelSubscription); + + return channelSubscription; +} + +export default updateChannelOverwrites; diff --git a/frontend/src/api/actions/updateChannelSettings.ts b/frontend/src/api/actions/updateChannelSettings.ts deleted file mode 100644 index d7b059ea..00000000 --- a/frontend/src/api/actions/updateChannelSettings.ts +++ /dev/null @@ -1,39 +0,0 @@ -import defaultHeaders from '../../configuration/defaultHeaders'; -import getApiUrl from '../../configuration/getApiUrl'; -import getFetchCredentials from '../../configuration/getFetchCredentials'; -import getCookie from '../../functions/getCookie'; - -export type ChannelAboutConfigType = { - index_playlists?: boolean; - download_format?: boolean | string; - autodelete_days?: boolean | number; - integrate_sponsorblock?: boolean | null; - subscriptions_channel_size?: number; - subscriptions_live_channel_size?: number; - subscriptions_shorts_channel_size?: number; -}; - -const updateChannelSettings = async (channelId: string, config: ChannelAboutConfigType) => { - const apiUrl = getApiUrl(); - const csrfCookie = getCookie('csrftoken'); - - const response = await fetch(`${apiUrl}/api/channel/${channelId}/`, { - method: 'POST', - headers: { - ...defaultHeaders, - 'X-CSRFToken': csrfCookie || '', - }, - credentials: getFetchCredentials(), - - body: JSON.stringify({ - channel_overwrites: config, - }), - }); - - const channelSubscription = await response.json(); - console.log('updateChannelSettings', channelSubscription); - - return channelSubscription; -}; - -export default updateChannelSettings; diff --git a/frontend/src/pages/ChannelAbout.tsx b/frontend/src/pages/ChannelAbout.tsx index 4123a063..8b1cda5a 100644 --- a/frontend/src/pages/ChannelAbout.tsx +++ b/frontend/src/pages/ChannelAbout.tsx @@ -11,17 +11,7 @@ import formatDate from '../functions/formatDates'; import PaginationDummy from '../components/PaginationDummy'; import FormattedNumber from '../components/FormattedNumber'; import Button from '../components/Button'; -import updateChannelSettings, { - ChannelAboutConfigType, -} from '../api/actions/updateChannelSettings'; - -const toStringToBool = (str: string) => { - try { - return JSON.parse(str); - } catch { - return null; - } -}; +import updateChannelOverwrites from '../api/actions/updateChannelOverwrite'; export type ChannelBaseOutletContextType = { isAdmin: boolean; @@ -52,59 +42,65 @@ const ChannelAbout = () => { const [refresh, setRefresh] = useState(true); const [channelResponse, setChannelResponse] = useState(); - const [channelConfig, setChannelConfig] = useState(); - const [downloadFormat, setDownloadFormat] = useState(channelConfig?.download_format); - const [autoDeleteAfter, setAutoDeleteAfter] = useState(channelConfig?.autodelete_days); - const [indexPlaylists, setIndexPlaylists] = useState( - channelConfig?.index_playlists ? 'true' : 'false', - ); - const [enableSponsorblock, setEnableSponsorblock] = useState( - channelConfig?.integrate_sponsorblock, - ); - const [pageSizeVideo, setPageSizeVideo] = useState(channelConfig?.subscriptions_channel_size); - const [pageSizeStreams, setPageSizeStreams] = useState( - channelConfig?.subscriptions_live_channel_size, - ); - const [pageSizeShorts, setPageSizeShorts] = useState( - channelConfig?.subscriptions_shorts_channel_size, - ); + const [downloadFormat, setDownloadFormat] = useState(""); + const [autoDeleteAfter, setAutoDeleteAfter] = useState(null); + const [indexPlaylists, setIndexPlaylists] = useState(false); + const [enableSponsorblock, setEnableSponsorblock] = useState(null) + const [pageSizeVideo, setPageSizeVideo] = useState(null) + const [pageSizeShorts, setPageSizeShorts] = useState(null) + const [pageSizeStreams, setPageSizeStreams] = useState(null) const channel = channelResponse?.data; - const channelOverwrites = channel?.channel_overwrites; - - const handleSubmit = async (event: { preventDefault: () => void }) => { - event.preventDefault(); - - await updateChannelSettings(channelId, { - index_playlists: toStringToBool(indexPlaylists), - download_format: downloadFormat, - autodelete_days: autoDeleteAfter, - integrate_sponsorblock: enableSponsorblock, - subscriptions_channel_size: pageSizeVideo, - subscriptions_live_channel_size: pageSizeStreams, - subscriptions_shorts_channel_size: pageSizeShorts, - }); - - setRefresh(true); - }; - useEffect(() => { (async () => { if (refresh) { const channelResponse = await loadChannelById(channelId); setChannelResponse(channelResponse); - setChannelConfig(channelResponse?.data?.channel_overwrites); - console.log('channel_overwrites', channelResponse?.data); - console.log('channel_overwrites', channelResponse?.data?.channel_overwrites); - console.log('channel_overwrites', '--------'); + setDownloadFormat(channelResponse?.data?.channel_overwrites?.download_format || "") + setAutoDeleteAfter(channelResponse?.data?.channel_overwrites?.autodelete_days) + setIndexPlaylists(channelResponse?.data?.channel_overwrites?.index_playlists || false) + setEnableSponsorblock(channelResponse?.data?.channel_overwrites?.integrate_sponsorblock) + setPageSizeVideo(channelResponse?.data?.channel_overwrites?.subscriptions_channel_size) + setPageSizeShorts(channelResponse?.data?.channel_overwrites?.subscriptions_shorts_channel_size) + setPageSizeStreams(channelResponse?.data?.channel_overwrites?.subscriptions_live_channel_size) + setRefresh(false); } })(); }, [refresh, channelId]); + const handleUpdateConfig = async (configKey: string, configValue: any) => { + if (!channel) return + await updateChannelOverwrites(channel.channel_id, configKey, configValue) + setRefresh(true) + } + + const handleNumberChange = ( + setValue: React.Dispatch> + ) => (e: React.ChangeEvent) => { + const inputValue = e.target.value; + + if (inputValue === "") { + setValue(null); + } else { + const numericValue = Number(inputValue); + setValue(isNaN(numericValue) ? null : numericValue); + } + }; + + const handleToggleSponsorBlock = async (isEnabled: boolean) => { + if (isEnabled) { + setEnableSponsorblock(true); + await handleUpdateConfig('integrate_sponsorblock', false); + } else { + await handleUpdateConfig('integrate_sponsorblock', null); + setEnableSponsorblock(null); + } + } + if (!channel) { return 'Channel not found!'; } @@ -242,195 +238,196 @@ const ChannelAbout = () => { )} {isAdmin && ( -
-
-

Customize {channel.channel_name}

-
-
-

- Download format:{' '} - - {channelOverwrites?.download_format || 'False'} - -

+
+
+

Channel Customization

+
+
+

Download Format

+
+
{ - const value = event.currentTarget.value; - - setDownloadFormat(value); + setDownloadFormat(event.target.value); }} /> - + + + )} + {channel.channel_overwrites?.download_format !== undefined && ( + + )} +
-
-

- Auto delete watched videos after x days:{' '} - - {channelOverwrites?.autodelete_days || 'False'} - -

+
+
+
+

Auto delete watched videos after x days

+
+
{ - const value = Number(event.currentTarget.value); - - if (value === 0) { - setAutoDeleteAfter(false); - } else { - setAutoDeleteAfter(Number(value)); - } - }} + type='number' + name='autodelete_days' + value={autoDeleteAfter ?? ""} + onChange={handleNumberChange(setAutoDeleteAfter)} /> - -
+
+ {autoDeleteAfter !== null && autoDeleteAfter !== channel.channel_overwrites?.autodelete_days && ( + <> + + + + )} + {channel.channel_overwrites?.autodelete_days !== undefined && ( + + )} +
- -
-

- Index playlists:{' '} - - {JSON.stringify(channelOverwrites?.index_playlists)} - -

- - - -
+
+
+
+

Index playlists

- -
-

- Enable{' '} - - SponsorBlock - - :{' '} - - {JSON.stringify(channelOverwrites?.integrate_sponsorblock)} - -

- +
+
+
+ { + handleUpdateConfig('index_playlists', event.target.checked || null) + }} + /> + {!indexPlaylists && ( + + )} + {indexPlaylists && ( + + )} +
+
-

Page Size Overrides

-
-

- Disable standard videos, shorts, or streams for this channel by setting their page - size to 0 (zero). -

-
-

Disable page size overwrite for channel by setting to negative value.

-
-
-

- YouTube page size:{' '} - - {channelOverwrites?.subscriptions_channel_size || 'False'} - -

- - Videos to scan to find new items for the Rescan subscriptions task, max - recommended 50. - -
+
+
+
+

Overwrite SponsorBlock

+
+
+ {enableSponsorblock !== undefined ? ( +
+
+ { + handleUpdateConfig('integrate_sponsorblock', event.target.checked) + }} + /> + {!enableSponsorblock && ( + + )} + {enableSponsorblock && ( + + )} +
+ +
+ ) : ( + + )} +
+
+

Page Size Overrides

+

Disable standard videos, shorts, or streams for this channel by setting their page size to 0 (zero).

+
+
+

Videos page size

+
+
{ - setPageSizeVideo(Number(event.currentTarget.value)); - }} + type='number' + name='subscriptions_channel_size' + value={pageSizeVideo ?? ""} + onChange={handleNumberChange(setPageSizeVideo)} /> -
+
+ {pageSizeVideo !== null && pageSizeVideo !== channel.channel_overwrites?.subscriptions_channel_size && ( + <> + + + + )} + {channel.channel_overwrites?.subscriptions_channel_size !== undefined && ( + + )} +
-
-

- YouTube Live page size:{' '} - - {channelOverwrites?.subscriptions_live_channel_size || 'False'} - -

- - Live Videos to scan to find new items for the Rescan subscriptions task, - max recommended 50. - -
+
+
+
+

Shorts page size

+
+
{ - setPageSizeStreams(Number(event.currentTarget.value)); - }} + type='number' + name='subscriptions_shorts_channel_size' + value={pageSizeShorts ?? ""} + onChange={handleNumberChange(setPageSizeShorts)} /> -
+
+ {pageSizeShorts !== null && pageSizeShorts !== channel.channel_overwrites?.subscriptions_shorts_channel_size && ( + <> + + + + )} + {channel.channel_overwrites?.subscriptions_shorts_channel_size !== undefined && ( + + )} +
-
-

- YouTube Shorts page size:{' '} - - {channelOverwrites?.subscriptions_shorts_channel_size || 'False'} - -

- - Shorts Videos to scan to find new items for the Rescan subscriptions{' '} - task, max recommended 50. - -
+
+
+
+

Live streams page size

+
+
{ - setPageSizeShorts(Number(event.currentTarget.value)); - }} + type='number' + name='subscriptions_live_channel_size' + value={pageSizeStreams ?? ""} + onChange={handleNumberChange(setPageSizeStreams)} /> +
+ {pageSizeStreams !== null && pageSizeStreams !== channel.channel_overwrites?.subscriptions_live_channel_size && ( + <> + + + + )} + {channel.channel_overwrites?.subscriptions_live_channel_size !== undefined && ( + + )} +
-
- -
)} diff --git a/frontend/src/style.css b/frontend/src/style.css index 060716d5..5880a331 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -129,6 +129,8 @@ button:hover { .button-box { padding: 5px 0; + display: inline-flex; + gap: 5px; } .unsubscribe { @@ -684,28 +686,6 @@ video:-webkit-full-screen { margin-bottom: 10px; } -.overwrite-form { - display: grid; - grid-template-columns: 1fr 1fr; - width: 100%; -} - -.overwrite-form button { - width: 200px; -} - -.overwrite-form-item { - margin-bottom: 1rem; -} - -.overwrite-form-item input { - width: 90%; -} - -.hidden-overwrite { - display: none; -} - /* login */ .login-page { display: flex; @@ -1085,6 +1065,18 @@ video:-webkit-full-screen { color: #fff; } +/* channel settings */ +.settings-box-wrapper { + display: grid; + grid-template-columns: 33% 1fr; + width: 100%; + align-items: center; +} + +.settings-box-wrapper input:not([type='checkbox']) { + min-width: 300px; +} + /* settings */ .settings-group { background-color: var(--highlight-bg);