1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge pull request #6231 from gnattu/fix-parental-tags

This commit is contained in:
Bill Thornton 2024-10-20 15:31:02 -04:00 committed by GitHub
commit 38ab2c168b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 75 additions and 93 deletions

View file

@ -146,48 +146,6 @@ const UserParentalControl = () => {
blockUnratedItems.dispatchEvent(new CustomEvent('create')); blockUnratedItems.dispatchEvent(new CustomEvent('create'));
}, []); }, []);
const loadAllowedTags = useCallback((tags: string[]) => {
const page = element.current;
if (!page) {
console.error('[userparentalcontrol] Unexpected null page reference');
return;
}
setAllowedTags(tags);
const allowedTagsElem = page.querySelector('.allowedTags') as HTMLDivElement;
for (const btnDeleteTag of allowedTagsElem.querySelectorAll('.btnDeleteTag')) {
btnDeleteTag.addEventListener('click', function () {
const tag = btnDeleteTag.getAttribute('data-tag');
const newTags = tags.filter(t => t !== tag);
loadAllowedTags(newTags);
});
}
}, []);
const loadBlockedTags = useCallback((tags: string[]) => {
const page = element.current;
if (!page) {
console.error('[userparentalcontrol] Unexpected null page reference');
return;
}
setBlockedTags(tags);
const blockedTagsElem = page.querySelector('.blockedTags') as HTMLDivElement;
for (const btnDeleteTag of blockedTagsElem.querySelectorAll('.btnDeleteTag')) {
btnDeleteTag.addEventListener('click', function () {
const tag = btnDeleteTag.getAttribute('data-tag');
const newTags = tags.filter(t => t !== tag);
loadBlockedTags(newTags);
});
}
}, []);
const loadUser = useCallback((user: UserDto, allParentalRatings: ParentalRating[]) => { const loadUser = useCallback((user: UserDto, allParentalRatings: ParentalRating[]) => {
const page = element.current; const page = element.current;
@ -200,8 +158,8 @@ const UserParentalControl = () => {
void libraryMenu.then(menu => menu.setTitle(user.Name)); void libraryMenu.then(menu => menu.setTitle(user.Name));
loadUnratedItems(user); loadUnratedItems(user);
loadAllowedTags(user.Policy?.AllowedTags || []); setAllowedTags(user.Policy?.AllowedTags || []);
loadBlockedTags(user.Policy?.BlockedTags || []); setBlockedTags(user.Policy?.BlockedTags || []);
populateRatings(allParentalRatings); populateRatings(allParentalRatings);
let ratingValue = ''; let ratingValue = '';
@ -222,7 +180,7 @@ const UserParentalControl = () => {
} }
setAccessSchedules(user.Policy?.AccessSchedules || []); setAccessSchedules(user.Policy?.AccessSchedules || []);
loading.hide(); loading.hide();
}, [loadAllowedTags, loadBlockedTags, loadUnratedItems, populateRatings]); }, [libraryMenu, setAllowedTags, setBlockedTags, loadUnratedItems, populateRatings]);
const loadData = useCallback(() => { const loadData = useCallback(() => {
if (!userId) { if (!userId) {
@ -296,7 +254,7 @@ const UserParentalControl = () => {
if (tags.indexOf(value) == -1) { if (tags.indexOf(value) == -1) {
tags.push(value); tags.push(value);
loadAllowedTags(tags); setAllowedTags(tags);
} }
}).catch(() => { }).catch(() => {
// prompt closed // prompt closed
@ -317,7 +275,7 @@ const UserParentalControl = () => {
if (tags.indexOf(value) == -1) { if (tags.indexOf(value) == -1) {
tags.push(value); tags.push(value);
loadBlockedTags(tags); setBlockedTags(tags);
} }
}).catch(() => { }).catch(() => {
// prompt closed // prompt closed
@ -348,7 +306,8 @@ const UserParentalControl = () => {
return false; return false;
}; };
(page.querySelector('#btnAddSchedule') as HTMLButtonElement).addEventListener('click', function () { // The following is still hacky and should migrate to pure react implementation for callbacks in the future
const accessSchedulesPopupCallback = function () {
showSchedulePopup({ showSchedulePopup({
Id: 0, Id: 0,
UserId: '', UserId: '',
@ -356,37 +315,19 @@ const UserParentalControl = () => {
StartHour: 0, StartHour: 0,
EndHour: 0 EndHour: 0
}, -1); }, -1);
}); };
(page.querySelector('#btnAddSchedule') as HTMLButtonElement).addEventListener('click', accessSchedulesPopupCallback);
(page.querySelector('#btnAddAllowedTag') as HTMLButtonElement).addEventListener('click', function () { (page.querySelector('#btnAddAllowedTag') as HTMLButtonElement).addEventListener('click', showAllowedTagPopup);
showAllowedTagPopup(); (page.querySelector('#btnAddBlockedTag') as HTMLButtonElement).addEventListener('click', showBlockedTagPopup);
});
(page.querySelector('#btnAddBlockedTag') as HTMLButtonElement).addEventListener('click', function () {
showBlockedTagPopup();
});
(page.querySelector('.userParentalControlForm') as HTMLFormElement).addEventListener('submit', onSubmit); (page.querySelector('.userParentalControlForm') as HTMLFormElement).addEventListener('submit', onSubmit);
}, [loadAllowedTags, loadBlockedTags, loadData, userId]);
useEffect(() => { return () => {
const page = element.current; (page.querySelector('#btnAddSchedule') as HTMLButtonElement).removeEventListener('click', accessSchedulesPopupCallback);
(page.querySelector('#btnAddAllowedTag') as HTMLButtonElement).removeEventListener('click', showAllowedTagPopup);
if (!page) { (page.querySelector('#btnAddBlockedTag') as HTMLButtonElement).removeEventListener('click', showBlockedTagPopup);
console.error('[userparentalcontrol] Unexpected null page reference'); (page.querySelector('.userParentalControlForm') as HTMLFormElement).removeEventListener('submit', onSubmit);
return; };
} }, [setAllowedTags, setBlockedTags, loadData, userId]);
const accessScheduleList = page.querySelector('.accessScheduleList') as HTMLDivElement;
for (const btnDelete of accessScheduleList.querySelectorAll('.btnDelete')) {
btnDelete.addEventListener('click', function () {
const index = parseInt(btnDelete.getAttribute('data-index') ?? '0', 10);
const newindex = accessSchedules.filter((_e, i) => i != index);
setAccessSchedules(newindex);
});
}
}, [accessSchedules]);
const optionMaxParentalRating = () => { const optionMaxParentalRating = () => {
let content = ''; let content = '';
@ -397,6 +338,21 @@ const UserParentalControl = () => {
return content; return content;
}; };
const removeAllowedTagsCallback = useCallback((tag: string) => {
const newTags = allowedTags.filter(t => t !== tag);
setAllowedTags(newTags);
}, [allowedTags, setAllowedTags]);
const removeBlockedTagsTagsCallback = useCallback((tag: string) => {
const newTags = blockedTags.filter(t => t !== tag);
setBlockedTags(newTags);
}, [blockedTags, setBlockedTags]);
const removeScheduleCallback = useCallback((index: number) => {
const newSchedules = accessSchedules.filter((_e, i) => i != index);
setAccessSchedules(newSchedules);
}, [accessSchedules, setAccessSchedules]);
return ( return (
<Page <Page
id='userParentalControlPage' id='userParentalControlPage'
@ -461,6 +417,7 @@ const UserParentalControl = () => {
key={tag} key={tag}
tag={tag} tag={tag}
tagType='allowedTag' tagType='allowedTag'
removeTagCallback={removeAllowedTagsCallback}
/>; />;
})} })}
</div> </div>
@ -485,6 +442,7 @@ const UserParentalControl = () => {
key={tag} key={tag}
tag={tag} tag={tag}
tagType='blockedTag' tagType='blockedTag'
removeTagCallback={removeBlockedTagsTagsCallback}
/>; />;
})} })}
</div> </div>
@ -503,11 +461,12 @@ const UserParentalControl = () => {
<div className='accessScheduleList paperList'> <div className='accessScheduleList paperList'>
{accessSchedules.map((accessSchedule, index) => { {accessSchedules.map((accessSchedule, index) => {
return <AccessScheduleList return <AccessScheduleList
key={accessSchedule.Id} key={`${accessSchedule.DayOfWeek}${accessSchedule.StartHour}${accessSchedule.EndHour}`}
index={index} index={index}
DayOfWeek={accessSchedule.DayOfWeek} DayOfWeek={accessSchedule.DayOfWeek}
StartHour={accessSchedule.StartHour} StartHour={accessSchedule.StartHour}
EndHour={accessSchedule.EndHour} EndHour={accessSchedule.EndHour}
removeScheduleCallback={removeScheduleCallback}
/>; />;
})} })}
</div> </div>

View file

@ -1,4 +1,4 @@
import React, { FunctionComponent } from 'react'; import React, { FunctionComponent, useCallback } from 'react';
import datetime from '../../../scripts/datetime'; import datetime from '../../../scripts/datetime';
import globalize from '../../../lib/globalize'; import globalize from '../../../lib/globalize';
import IconButtonElement from '../../../elements/IconButtonElement'; import IconButtonElement from '../../../elements/IconButtonElement';
@ -8,6 +8,7 @@ type AccessScheduleListProps = {
DayOfWeek?: string; DayOfWeek?: string;
StartHour?: number ; StartHour?: number ;
EndHour?: number; EndHour?: number;
removeScheduleCallback?: (index: number) => void;
}; };
function getDisplayTime(hours = 0) { function getDisplayTime(hours = 0) {
@ -21,7 +22,10 @@ function getDisplayTime(hours = 0) {
return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0)); return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0));
} }
const AccessScheduleList: FunctionComponent<AccessScheduleListProps> = ({ index, DayOfWeek, StartHour, EndHour }: AccessScheduleListProps) => { const AccessScheduleList: FunctionComponent<AccessScheduleListProps> = ({ index, DayOfWeek, StartHour, EndHour, removeScheduleCallback }: AccessScheduleListProps) => {
const onClick = useCallback(() => {
index !== undefined && removeScheduleCallback !== undefined && removeScheduleCallback(index);
}, [index, removeScheduleCallback]);
return ( return (
<div <div
className='liSchedule listItem' className='liSchedule listItem'
@ -43,6 +47,7 @@ const AccessScheduleList: FunctionComponent<AccessScheduleListProps> = ({ index,
title='Delete' title='Delete'
icon='delete' icon='delete'
dataIndex={index} dataIndex={index}
onClick={onClick}
/> />
</div> </div>
); );

View file

@ -1,12 +1,16 @@
import React, { FunctionComponent } from 'react'; import React, { FunctionComponent, useCallback } from 'react';
import IconButtonElement from '../../../elements/IconButtonElement'; import IconButtonElement from '../../../elements/IconButtonElement';
type IProps = { type IProps = {
tag?: string, tag?: string,
tagType?: string; tagType?: string;
removeTagCallback?: (tag: string) => void;
}; };
const TagList: FunctionComponent<IProps> = ({ tag, tagType }: IProps) => { const TagList: FunctionComponent<IProps> = ({ tag, tagType, removeTagCallback }: IProps) => {
const onClick = useCallback(() => {
tag !== undefined && removeTagCallback !== undefined && removeTagCallback(tag);
}, [tag, removeTagCallback]);
return ( return (
<div className='paperList'> <div className='paperList'>
<div className='listItem'> <div className='listItem'>
@ -21,6 +25,7 @@ const TagList: FunctionComponent<IProps> = ({ tag, tagType }: IProps) => {
title='Delete' title='Delete'
icon='delete' icon='delete'
dataTag={tag} dataTag={tag}
onClick={onClick}
/> />
</div> </div>
</div> </div>

View file

@ -11,6 +11,7 @@ type IProps = {
dataIndex?: string | number; dataIndex?: string | number;
dataTag?: string | number; dataTag?: string | number;
dataProfileid?: string | number; dataProfileid?: string | number;
onClick?: () => void;
}; };
const createIconButtonElement = ({ is, id, className, title, icon, dataIndex, dataTag, dataProfileid }: IProps) => ({ const createIconButtonElement = ({ is, id, className, title, icon, dataIndex, dataTag, dataProfileid }: IProps) => ({
@ -28,19 +29,31 @@ const createIconButtonElement = ({ is, id, className, title, icon, dataIndex, da
</button>` </button>`
}); });
const IconButtonElement: FunctionComponent<IProps> = ({ is, id, className, title, icon, dataIndex, dataTag, dataProfileid }: IProps) => { const IconButtonElement: FunctionComponent<IProps> = ({ is, id, className, title, icon, dataIndex, dataTag, dataProfileid, onClick }: IProps) => {
const button = createIconButtonElement({
is: is,
id: id ? `id="${id}"` : '',
className: className,
title: title ? `title="${globalize.translate(title)}"` : '',
icon: icon,
dataIndex: (dataIndex || dataIndex === 0) ? `data-index="${dataIndex}"` : '',
dataTag: dataTag ? `data-tag="${dataTag}"` : '',
dataProfileid: dataProfileid ? `data-profileid="${dataProfileid}"` : ''
});
if (onClick !== undefined) {
return (
<button
style={{ all: 'unset' }}
dangerouslySetInnerHTML={button}
onClick={onClick}
/>
);
}
return ( return (
<div <div
dangerouslySetInnerHTML={createIconButtonElement({ dangerouslySetInnerHTML={button}
is: is,
id: id ? `id="${id}"` : '',
className: className,
title: title ? `title="${globalize.translate(title)}"` : '',
icon: icon,
dataIndex: (dataIndex || dataIndex === 0) ? `data-index="${dataIndex}"` : '',
dataTag: dataTag ? `data-tag="${dataTag}"` : '',
dataProfileid: dataProfileid ? `data-profileid="${dataProfileid}"` : ''
})}
/> />
); );
}; };