diff --git a/src/apps/dashboard/routes/users/parentalcontrol.tsx b/src/apps/dashboard/routes/users/parentalcontrol.tsx index da7262af1b..20e338b603 100644 --- a/src/apps/dashboard/routes/users/parentalcontrol.tsx +++ b/src/apps/dashboard/routes/users/parentalcontrol.tsx @@ -146,48 +146,6 @@ const UserParentalControl = () => { 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 page = element.current; @@ -200,8 +158,8 @@ const UserParentalControl = () => { void libraryMenu.then(menu => menu.setTitle(user.Name)); loadUnratedItems(user); - loadAllowedTags(user.Policy?.AllowedTags || []); - loadBlockedTags(user.Policy?.BlockedTags || []); + setAllowedTags(user.Policy?.AllowedTags || []); + setBlockedTags(user.Policy?.BlockedTags || []); populateRatings(allParentalRatings); let ratingValue = ''; @@ -222,7 +180,7 @@ const UserParentalControl = () => { } setAccessSchedules(user.Policy?.AccessSchedules || []); loading.hide(); - }, [loadAllowedTags, loadBlockedTags, loadUnratedItems, populateRatings]); + }, [libraryMenu, setAllowedTags, setBlockedTags, loadUnratedItems, populateRatings]); const loadData = useCallback(() => { if (!userId) { @@ -296,7 +254,7 @@ const UserParentalControl = () => { if (tags.indexOf(value) == -1) { tags.push(value); - loadAllowedTags(tags); + setAllowedTags(tags); } }).catch(() => { // prompt closed @@ -317,7 +275,7 @@ const UserParentalControl = () => { if (tags.indexOf(value) == -1) { tags.push(value); - loadBlockedTags(tags); + setBlockedTags(tags); } }).catch(() => { // prompt closed @@ -348,7 +306,8 @@ const UserParentalControl = () => { 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({ Id: 0, UserId: '', @@ -356,37 +315,19 @@ const UserParentalControl = () => { StartHour: 0, EndHour: 0 }, -1); - }); - - (page.querySelector('#btnAddAllowedTag') as HTMLButtonElement).addEventListener('click', function () { - showAllowedTagPopup(); - }); - - (page.querySelector('#btnAddBlockedTag') as HTMLButtonElement).addEventListener('click', function () { - showBlockedTagPopup(); - }); - + }; + (page.querySelector('#btnAddSchedule') as HTMLButtonElement).addEventListener('click', accessSchedulesPopupCallback); + (page.querySelector('#btnAddAllowedTag') as HTMLButtonElement).addEventListener('click', showAllowedTagPopup); + (page.querySelector('#btnAddBlockedTag') as HTMLButtonElement).addEventListener('click', showBlockedTagPopup); (page.querySelector('.userParentalControlForm') as HTMLFormElement).addEventListener('submit', onSubmit); - }, [loadAllowedTags, loadBlockedTags, loadData, userId]); - useEffect(() => { - const page = element.current; - - if (!page) { - console.error('[userparentalcontrol] Unexpected null page reference'); - return; - } - - 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]); + return () => { + (page.querySelector('#btnAddSchedule') as HTMLButtonElement).removeEventListener('click', accessSchedulesPopupCallback); + (page.querySelector('#btnAddAllowedTag') as HTMLButtonElement).removeEventListener('click', showAllowedTagPopup); + (page.querySelector('#btnAddBlockedTag') as HTMLButtonElement).removeEventListener('click', showBlockedTagPopup); + (page.querySelector('.userParentalControlForm') as HTMLFormElement).removeEventListener('submit', onSubmit); + }; + }, [setAllowedTags, setBlockedTags, loadData, userId]); const optionMaxParentalRating = () => { let content = ''; @@ -397,6 +338,21 @@ const UserParentalControl = () => { 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 ( { key={tag} tag={tag} tagType='allowedTag' + removeTagCallback={removeAllowedTagsCallback} />; })} @@ -485,6 +442,7 @@ const UserParentalControl = () => { key={tag} tag={tag} tagType='blockedTag' + removeTagCallback={removeBlockedTagsTagsCallback} />; })} @@ -503,11 +461,12 @@ const UserParentalControl = () => {
{accessSchedules.map((accessSchedule, index) => { return ; })}
diff --git a/src/components/dashboard/users/AccessScheduleList.tsx b/src/components/dashboard/users/AccessScheduleList.tsx index 7303ec7e50..91a789017f 100644 --- a/src/components/dashboard/users/AccessScheduleList.tsx +++ b/src/components/dashboard/users/AccessScheduleList.tsx @@ -1,4 +1,4 @@ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, useCallback } from 'react'; import datetime from '../../../scripts/datetime'; import globalize from '../../../lib/globalize'; import IconButtonElement from '../../../elements/IconButtonElement'; @@ -8,6 +8,7 @@ type AccessScheduleListProps = { DayOfWeek?: string; StartHour?: number ; EndHour?: number; + removeScheduleCallback?: (index: number) => void; }; function getDisplayTime(hours = 0) { @@ -21,7 +22,10 @@ function getDisplayTime(hours = 0) { return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0)); } -const AccessScheduleList: FunctionComponent = ({ index, DayOfWeek, StartHour, EndHour }: AccessScheduleListProps) => { +const AccessScheduleList: FunctionComponent = ({ index, DayOfWeek, StartHour, EndHour, removeScheduleCallback }: AccessScheduleListProps) => { + const onClick = useCallback(() => { + index !== undefined && removeScheduleCallback !== undefined && removeScheduleCallback(index); + }, [index, removeScheduleCallback]); return (
= ({ index, title='Delete' icon='delete' dataIndex={index} + onClick={onClick} />
); diff --git a/src/components/dashboard/users/TagList.tsx b/src/components/dashboard/users/TagList.tsx index 531ee2f6e6..172ee0196e 100644 --- a/src/components/dashboard/users/TagList.tsx +++ b/src/components/dashboard/users/TagList.tsx @@ -1,12 +1,16 @@ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, useCallback } from 'react'; import IconButtonElement from '../../../elements/IconButtonElement'; type IProps = { tag?: string, tagType?: string; + removeTagCallback?: (tag: string) => void; }; -const TagList: FunctionComponent = ({ tag, tagType }: IProps) => { +const TagList: FunctionComponent = ({ tag, tagType, removeTagCallback }: IProps) => { + const onClick = useCallback(() => { + tag !== undefined && removeTagCallback !== undefined && removeTagCallback(tag); + }, [tag, removeTagCallback]); return (
@@ -21,6 +25,7 @@ const TagList: FunctionComponent = ({ tag, tagType }: IProps) => { title='Delete' icon='delete' dataTag={tag} + onClick={onClick} />
diff --git a/src/elements/IconButtonElement.tsx b/src/elements/IconButtonElement.tsx index 93e3fd2b87..300e442c98 100644 --- a/src/elements/IconButtonElement.tsx +++ b/src/elements/IconButtonElement.tsx @@ -11,6 +11,7 @@ type IProps = { dataIndex?: string | number; dataTag?: string | number; dataProfileid?: string | number; + onClick?: () => void; }; const createIconButtonElement = ({ is, id, className, title, icon, dataIndex, dataTag, dataProfileid }: IProps) => ({ @@ -28,19 +29,31 @@ const createIconButtonElement = ({ is, id, className, title, icon, dataIndex, da ` }); -const IconButtonElement: FunctionComponent = ({ is, id, className, title, icon, dataIndex, dataTag, dataProfileid }: IProps) => { +const IconButtonElement: FunctionComponent = ({ 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 ( +