Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use LinkButton instead of dangerouslySetInnerHTML #5226

Merged
merged 10 commits into from
Sep 23, 2024
17 changes: 7 additions & 10 deletions src/apps/dashboard/routes/users/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import LibraryMenu from '../../../../scripts/libraryMenu';
import ButtonElement from '../../../../elements/ButtonElement';
import CheckBoxElement from '../../../../elements/CheckBoxElement';
import InputElement from '../../../../elements/InputElement';
import LinkEditUserPreferences from '../../../../components/dashboard/users/LinkEditUserPreferences';
import LinkButton from '../../../../elements/emby-button/LinkButton';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import SectionTabs from '../../../../components/dashboard/users/SectionTabs';
import loading from '../../../../components/loading/loading';
Expand Down Expand Up @@ -38,7 +38,7 @@ function onSaveComplete() {
const UserEdit = () => {
const [ searchParams ] = useSearchParams();
const userId = searchParams.get('userId');
const [ userName, setUserName ] = useState('');
const [ userDto, setUserDto ] = useState<UserDto>();
const [ deleteFoldersAccess, setDeleteFoldersAccess ] = useState<ResetProvider[]>([]);
const [ authProviders, setAuthProviders ] = useState<NameIdPair[]>([]);
const [ passwordResetProviders, setPasswordResetProviders ] = useState<NameIdPair[]>([]);
Expand Down Expand Up @@ -147,10 +147,8 @@ const UserEdit = () => {
txtUserName.disabled = false;
txtUserName.removeAttribute('disabled');

const lnkEditUserPreferences = page.querySelector('.lnkEditUserPreferences') as HTMLDivElement;
lnkEditUserPreferences.setAttribute('href', 'mypreferencesmenu.html?userId=' + user.Id);
LibraryMenu.setTitle(user.Name);
setUserName(user.Name || '');
setUserDto(user);
(page.querySelector('#txtUserName') as HTMLInputElement).value = user.Name || '';
(page.querySelector('.chkIsAdmin') as HTMLInputElement).checked = !!user.Policy?.IsAdministrator;
(page.querySelector('.chkDisabled') as HTMLInputElement).checked = !!user.Policy?.IsDisabled;
Expand Down Expand Up @@ -292,7 +290,7 @@ const UserEdit = () => {
<div ref={element} className='content-primary'>
<div className='verticalSection'>
<SectionTitleContainer
title={userName}
title={userDto?.Name || ''}
url='https://jellyfin.org/docs/general/server/users/'
/>
</div>
Expand All @@ -302,10 +300,9 @@ const UserEdit = () => {
className='lnkEditUserPreferencesContainer'
style={{ paddingBottom: '1em' }}
>
<LinkEditUserPreferences
className= 'lnkEditUserPreferences button-link'
title= 'ButtonEditOtherUserPreferences'
/>
<LinkButton className='lnkEditUserPreferences button-link' href={userDto?.Id ? `mypreferencesmenu.html?userId=${userDto.Id}` : undefined}>
{globalize.translate('ButtonEditOtherUserPreferences')}
</LinkButton>
</div>
<form className='editUserProfileForm'>
<div className='disabledUserBanner hide'>
Expand Down
30 changes: 0 additions & 30 deletions src/components/dashboard/users/LinkEditUserPreferences.tsx

This file was deleted.

77 changes: 44 additions & 33 deletions src/components/dashboard/users/SectionTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,60 @@
import React, { FunctionComponent } from 'react';

import globalize from 'lib/globalize';
import { navigate } from '../../../utils/dashboard';
import LinkButton from '../../../elements/emby-button/LinkButton';

type IProps = {
activeTab: string;
};

const createLinkElement = (activeTab: string) => ({
__html: `<a href="#"
is="emby-linkbutton"
data-role="button"
class="${activeTab === 'useredit' ? 'ui-btn-active' : ''}"
onclick="Dashboard.navigate('/dashboard/users/profile', true);">
${globalize.translate('Profile')}
</a>
<a href="#"
is="emby-linkbutton"
data-role="button"
class="${activeTab === 'userlibraryaccess' ? 'ui-btn-active' : ''}"
onclick="Dashboard.navigate('/dashboard/users/access', true);">
${globalize.translate('TabAccess')}
</a>
<a href="#"
is="emby-linkbutton"
data-role="button"
class="${activeTab === 'userparentalcontrol' ? 'ui-btn-active' : ''}"
onclick="Dashboard.navigate('/dashboard/users/parentalcontrol', true);">
${globalize.translate('TabParentalControl')}
</a>
<a href="#"
is="emby-linkbutton"
data-role="button"
class="${activeTab === 'userpassword' ? 'ui-btn-active' : ''}"
onclick="Dashboard.navigate('/dashboard/users/password', true);">
${globalize.translate('HeaderPassword')}
</a>`
});
function useNavigate(url: string): () => void {
return React.useCallback(() => {
navigate(url, true).catch(err => {
console.warn('Error navigating to dashboard url', err);
});
}, [url]);
}

const SectionTabs: FunctionComponent<IProps> = ({ activeTab }: IProps) => {
const onClickProfile = useNavigate('/dashboard/users/profile');
const onClickAccess = useNavigate('/dashboard/users/access');
const onClickParentalControl = useNavigate('/dashboard/users/parentalcontrol');
const clickPassword = useNavigate('/dashboard/users/password');
return (
<div
data-role='controlgroup'
data-type='horizontal'
className='localnav'
dangerouslySetInnerHTML={createLinkElement(activeTab)}
/>
className='localnav'>
<LinkButton
href='#'
data-role='button'
className={activeTab === 'useredit' ? 'ui-btn-active' : ''}
onClick={onClickProfile}>
{globalize.translate('Profile')}
</LinkButton>
<LinkButton
href='#'
data-role='button'
className={activeTab === 'userlibraryaccess' ? 'ui-btn-active' : ''}
onClick={onClickAccess}>
{globalize.translate('TabAccess')}
</LinkButton>
<LinkButton
href='#'
data-role='button'
className={activeTab === 'userparentalcontrol' ? 'ui-btn-active' : ''}
onClick={onClickParentalControl}>
{globalize.translate('TabParentalControl')}
</LinkButton>
<LinkButton
href='#'
data-role='button'
className={activeTab === 'userpassword' ? 'ui-btn-active' : ''}
onClick={clickPassword}>
{globalize.translate('HeaderPassword')}
</LinkButton>
</div>
);
};

Expand Down
33 changes: 11 additions & 22 deletions src/components/dashboard/users/UserCardBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,9 @@ import { formatDistanceToNow } from 'date-fns';
import { getLocaleWithSuffix } from '../../../utils/dateFnsLocale';
import globalize from '../../../lib/globalize';
import IconButtonElement from '../../../elements/IconButtonElement';
import escapeHTML from 'escape-html';
import LinkButton from '../../../elements/emby-button/LinkButton';
import { getDefaultBackgroundClass } from '../../cardbuilder/cardBuilderUtils';

const createLinkElement = ({ user, renderImgUrl }: { user: UserDto, renderImgUrl: string }) => ({
__html: `<a
is="emby-linkbutton"
class="cardContent"
href="#/dashboard/users/profile?userId=${user.Id}"
>
${renderImgUrl}
</a>`
});

type IProps = {
user?: UserDto;
};
Expand Down Expand Up @@ -55,22 +45,21 @@ const UserCardBox: FunctionComponent<IProps> = ({ user = {} }: IProps) => {
const lastSeen = getLastSeenText(user.LastActivityDate);

const renderImgUrl = imgUrl ?
`<div class='${imageClass}' style='background-image:url(${imgUrl})'></div>` :
`<div class='${imageClass} ${getDefaultBackgroundClass(user.Name)} flex align-items-center justify-content-center'>
<span class='material-icons cardImageIcon person' aria-hidden='true'></span>
</div>`;
<div className={imageClass} style={{ backgroundImage: `url(${imgUrl})` }} /> :
<div className={`${imageClass} ${getDefaultBackgroundClass(user.Name)} flex align-items-center justify-content-center`}>
<span className='material-icons cardImageIcon person' aria-hidden='true'></span>
</div>;

return (
<div data-userid={user.Id} data-username={user.Name} className={cssClass}>
<div className='cardBox visualCardBox'>
<div className='cardScalable visualCardBox-cardScalable'>
<div className='cardPadder cardPadder-square'></div>
<div
dangerouslySetInnerHTML={createLinkElement({
user: user,
renderImgUrl: renderImgUrl
})}
/>
<LinkButton
className='cardContent'
href={`#/dashboard/users/profile?userId=${user.Id}`}>
{renderImgUrl}
</LinkButton>
</div>
<div className='cardFooter visualCardBox-cardFooter'>
<div
Expand All @@ -83,7 +72,7 @@ const UserCardBox: FunctionComponent<IProps> = ({ user = {} }: IProps) => {
/>
</div>
<div className='cardText'>
<span>{escapeHTML(user.Name)}</span>
<span>{user.Name}</span>
</div>
<div className='cardText cardText-secondary'>
<span>{lastSeen != '' ? lastSeen : ''}</span>
Expand Down
29 changes: 14 additions & 15 deletions src/components/search/SearchSuggestions.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import React, { type FC } from 'react';
import { useSearchSuggestions } from 'hooks/searchHook';
import React, { FunctionComponent, useEffect, useState } from 'react';

Check failure on line 1 in src/components/search/SearchSuggestions.tsx

View workflow job for this annotation

GitHub Actions / Run eslint

'useEffect' is defined but never used

Check failure on line 1 in src/components/search/SearchSuggestions.tsx

View workflow job for this annotation

GitHub Actions / Run eslint

'useState' is defined but never used
github-actions[bot] marked this conversation as resolved.
Show resolved Hide resolved
github-actions[bot] marked this conversation as resolved.
Show resolved Hide resolved

import Loading from 'components/loading/LoadingComponent';
import { appRouter } from '../router/appRouter';
import globalize from '../../lib/globalize';
import LinkButton from 'elements/emby-button/LinkButton';
import { useSearchSuggestions } from 'hooks/searchHook/useSearchSuggestions';
import globalize from 'lib/globalize';
import LinkButton from '../../elements/emby-button/LinkButton';

import '../../elements/emby-button/emby-button';

interface SearchSuggestionsProps {
parentId?: string;
}
type SearchSuggestionsProps = {
parentId?: string | null;
};

const SearchSuggestions: FC<SearchSuggestionsProps> = ({ parentId }) => {
const { isLoading, data: suggestions } = useSearchSuggestions(parentId);
const SearchSuggestions: FunctionComponent<SearchSuggestionsProps> = ({ parentId }) => {
const { isLoading, data: suggestions } = useSearchSuggestions(parentId || undefined);

if (isLoading) return <Loading />;

Expand All @@ -27,15 +29,12 @@
</div>

<div className='searchSuggestionsList padded-left padded-right'>
{suggestions?.map((item) => (
<div key={`suggestion-${item.Id}`}>
{suggestions?.map(item => (
<div key={item.Id}>
<LinkButton
className='button-link'
style={{ display: 'inline-block', padding: '0.5em 1em' }}
href={appRouter.getRouteUrl(item)}
style={{
display: 'inline-block',
padding: '0.5em 1em'
}}
>
{item.Name}
</LinkButton>
Expand Down
13 changes: 8 additions & 5 deletions src/elements/SectionTitleContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { FunctionComponent } from 'react';
import IconButtonElement from './IconButtonElement';
import SectionTitleLinkElement from './SectionTitleLinkElement';
import LinkButton from './emby-button/LinkButton';
import globalize from 'lib/globalize';

type IProps = {
SectionClassName?: string;
Expand Down Expand Up @@ -28,11 +29,13 @@ const SectionTitleContainer: FunctionComponent<IProps> = ({ SectionClassName, ti
icon={btnIcon}
/>}

{isLinkVisible && <SectionTitleLinkElement
{isLinkVisible && <LinkButton
className='raised button-alt headerHelpButton'
title='Help'
url={url}
/>}
target='_blank'
rel='noopener noreferrer'
href={url}>
{globalize.translate('Help')}
</LinkButton>}

</div>
);
Expand Down
35 changes: 0 additions & 35 deletions src/elements/SectionTitleLinkElement.tsx

This file was deleted.

4 changes: 3 additions & 1 deletion src/elements/emby-button/LinkButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const LinkButton: React.FC<LinkButtonProps> = ({
isAutoHideEnabled,
href,
target,
onClick,
children,
...rest
}) => {
Expand All @@ -41,7 +42,8 @@ const LinkButton: React.FC<LinkButtonProps> = ({
} else {
e.preventDefault();
}
}, [ href, target ]);
onClick?.(e);
}, [ href, target, onClick ]);

if (isAutoHideEnabled === true && !appHost.supports('externallinks')) {
return null;
Expand Down
Loading