mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #3025 from grafixeyehero/convert-userprofilespage-to-react
convert UserProfilesPage to react
This commit is contained in:
commit
5c8122a9dd
7 changed files with 323 additions and 200 deletions
33
src/components/dashboard/users/SectionTitleButtonElement.tsx
Normal file
33
src/components/dashboard/users/SectionTitleButtonElement.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
const createButtonElement = ({ className, title, icon }) => ({
|
||||
__html: `<button
|
||||
is="emby-button"
|
||||
type="button"
|
||||
class="${className}"
|
||||
style="margin-left:1em;"
|
||||
title="${title}">
|
||||
<span class="material-icons ${icon}"></span>
|
||||
</button>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
title?: string;
|
||||
className?: string;
|
||||
icon?: string,
|
||||
}
|
||||
|
||||
const SectionTitleButtonElement: FunctionComponent<IProps> = ({ className, title, icon }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createButtonElement({
|
||||
className: className,
|
||||
title: globalize.translate(title),
|
||||
icon: icon
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionTitleButtonElement;
|
34
src/components/dashboard/users/SectionTitleLinkElement.tsx
Normal file
34
src/components/dashboard/users/SectionTitleLinkElement.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
const createLinkElement = ({ className, href, title }) => ({
|
||||
__html: `<a
|
||||
is="emby-linkbutton"
|
||||
rel="noopener noreferrer"
|
||||
class="${className}"
|
||||
target="_blank"
|
||||
href="${href}"
|
||||
>
|
||||
${title}
|
||||
</a>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
title?: string;
|
||||
className?: string;
|
||||
url?: string
|
||||
}
|
||||
|
||||
const SectionTitleLinkElement: FunctionComponent<IProps> = ({ className, title, url }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createLinkElement({
|
||||
className: className,
|
||||
title: globalize.translate(title),
|
||||
href: url
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionTitleLinkElement;
|
100
src/components/dashboard/users/UserCardBox.tsx
Normal file
100
src/components/dashboard/users/UserCardBox.tsx
Normal file
|
@ -0,0 +1,100 @@
|
|||
import React, { FunctionComponent } from 'react';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { localeWithSuffix } from '../../../scripts/dfnshelper';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import cardBuilder from '../../cardbuilder/cardBuilder';
|
||||
|
||||
const createLinkElement = ({ user, renderImgUrl }) => ({
|
||||
__html: `<a
|
||||
is="emby-linkbutton"
|
||||
class="cardContent"
|
||||
href="#!/useredit.html?userId=${user.Id}"
|
||||
>
|
||||
${renderImgUrl}
|
||||
</a>`
|
||||
});
|
||||
|
||||
const createButtonElement = () => ({
|
||||
__html: `<button
|
||||
is="paper-icon-button-light"
|
||||
type="button"
|
||||
class="btnUserMenu flex-shrink-zero"
|
||||
>
|
||||
<span class="material-icons more_vert"></span>
|
||||
</button>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
user?: Record<string, any>;
|
||||
}
|
||||
|
||||
const getLastSeenText = (lastActivityDate) => {
|
||||
if (lastActivityDate) {
|
||||
return globalize.translate('LastSeen', formatDistanceToNow(Date.parse(lastActivityDate), localeWithSuffix));
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
const UserCardBox: FunctionComponent<IProps> = ({ user = [] }: IProps) => {
|
||||
let cssClass = 'card squareCard scalableCard squareCard-scalable';
|
||||
|
||||
if (user.Policy.IsDisabled) {
|
||||
cssClass += ' grayscale';
|
||||
}
|
||||
|
||||
let imgUrl;
|
||||
|
||||
if (user.PrimaryImageTag) {
|
||||
imgUrl = window.ApiClient.getUserImageUrl(user.Id, {
|
||||
width: 300,
|
||||
tag: user.PrimaryImageTag,
|
||||
type: 'Primary'
|
||||
});
|
||||
}
|
||||
|
||||
let imageClass = 'cardImage';
|
||||
|
||||
if (user.Policy.IsDisabled) {
|
||||
imageClass += ' disabledUser';
|
||||
}
|
||||
|
||||
const lastSeen = getLastSeenText(user.LastActivityDate);
|
||||
|
||||
const renderImgUrl = imgUrl ?
|
||||
`<div class='${imageClass}' style='background-image:url(${imgUrl})'></div>` :
|
||||
`<div class='${imageClass} ${cardBuilder.getDefaultBackgroundClass(user.Name)} flex align-items-center justify-content-center'>
|
||||
<span class='material-icons cardImageIcon person'></span>
|
||||
</div>`;
|
||||
|
||||
return (
|
||||
<div data-userid={user.Id} className={cssClass}>
|
||||
<div className='cardBox visualCardBox'>
|
||||
<div className='cardScalable visualCardBox-cardScalable'>
|
||||
<div className='cardPadder cardPadder-square'></div>
|
||||
<div
|
||||
dangerouslySetInnerHTML={createLinkElement({
|
||||
user: user,
|
||||
renderImgUrl: renderImgUrl
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className='cardFooter visualCardBox-cardFooter'>
|
||||
<div className='cardText flex align-items-center'>
|
||||
<div className='flex-grow' style={{overflow: 'hidden', textOverflow: 'ellipsis'}}>
|
||||
{user.Name}
|
||||
</div>
|
||||
<div
|
||||
dangerouslySetInnerHTML={createButtonElement()}
|
||||
/>
|
||||
</div>
|
||||
<div className='cardText cardText-secondary'>
|
||||
{lastSeen != '' ? lastSeen : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserCardBox;
|
154
src/components/pages/UserProfilesPage.tsx
Normal file
154
src/components/pages/UserProfilesPage.tsx
Normal file
|
@ -0,0 +1,154 @@
|
|||
|
||||
import React, {FunctionComponent, useEffect, useState, useRef} from 'react';
|
||||
import Dashboard from '../../scripts/clientUtils';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import loading from '../loading/loading';
|
||||
import dom from '../../scripts/dom';
|
||||
import confirm from '../../components/confirm/confirm';
|
||||
import UserCardBox from '../dashboard/users/UserCardBox';
|
||||
import SectionTitleButtonElement from '../dashboard/users/SectionTitleButtonElement';
|
||||
import SectionTitleLinkElement from '../dashboard/users/SectionTitleLinkElement';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../components/cardbuilder/card.scss';
|
||||
import '../../components/indicators/indicators.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
|
||||
type MenuEntry = {
|
||||
name?: string;
|
||||
id?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
const UserProfilesPage: FunctionComponent = () => {
|
||||
const [ users, setUsers ] = useState([]);
|
||||
|
||||
const element = useRef(null);
|
||||
|
||||
const loadData = () => {
|
||||
loading.show();
|
||||
window.ApiClient.getUsers().then(function (result) {
|
||||
setUsers(result);
|
||||
loading.hide();
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
|
||||
const showUserMenu = (elem) => {
|
||||
const card = dom.parentWithClass(elem, 'card');
|
||||
const userId = card.getAttribute('data-userid');
|
||||
|
||||
const menuItems: MenuEntry[] = [];
|
||||
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonOpen'),
|
||||
id: 'open',
|
||||
icon: 'mode_edit'
|
||||
});
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonLibraryAccess'),
|
||||
id: 'access',
|
||||
icon: 'lock'
|
||||
});
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonParentalControl'),
|
||||
id: 'parentalcontrol',
|
||||
icon: 'person'
|
||||
});
|
||||
menuItems.push({
|
||||
name: globalize.translate('Delete'),
|
||||
id: 'delete',
|
||||
icon: 'delete'
|
||||
});
|
||||
|
||||
import('../../components/actionSheet/actionSheet').then(({default: actionsheet}) => {
|
||||
actionsheet.show({
|
||||
items: menuItems,
|
||||
positionTo: card,
|
||||
callback: function (id) {
|
||||
switch (id) {
|
||||
case 'open':
|
||||
Dashboard.navigate('useredit.html?userId=' + userId);
|
||||
break;
|
||||
|
||||
case 'access':
|
||||
Dashboard.navigate('userlibraryaccess.html?userId=' + userId);
|
||||
break;
|
||||
|
||||
case 'parentalcontrol':
|
||||
Dashboard.navigate('userparentalcontrol.html?userId=' + userId);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
deleteUser(userId);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const deleteUser = (id) => {
|
||||
const msg = globalize.translate('DeleteUserConfirmation');
|
||||
|
||||
confirm({
|
||||
title: globalize.translate('DeleteUser'),
|
||||
text: msg,
|
||||
confirmText: globalize.translate('Delete'),
|
||||
primary: 'delete'
|
||||
}).then(function () {
|
||||
loading.show();
|
||||
window.ApiClient.deleteUser(id).then(function () {
|
||||
loadData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
element?.current?.addEventListener('click', function (e) {
|
||||
const btnUserMenu = dom.parentWithClass(e.target, 'btnUserMenu');
|
||||
|
||||
if (btnUserMenu) {
|
||||
showUserMenu(btnUserMenu);
|
||||
}
|
||||
});
|
||||
|
||||
element?.current?.querySelector('.btnAddUser').addEventListener('click', function() {
|
||||
Dashboard.navigate('usernew.html');
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='content-primary'>
|
||||
<div className='verticalSection verticalSection-extrabottompadding'>
|
||||
<div
|
||||
className='sectionTitleContainer sectionTitleContainer-cards'
|
||||
style={{display: 'flex', alignItems: 'center', paddingBottom: '1em'}}
|
||||
>
|
||||
<h2 className='sectionTitle sectionTitle-cards'>
|
||||
{globalize.translate('HeaderUsers')}
|
||||
</h2>
|
||||
<SectionTitleButtonElement
|
||||
className='fab btnAddUser submit sectionTitleButton'
|
||||
title='ButtonAddUser'
|
||||
icon='add'
|
||||
/>
|
||||
<SectionTitleLinkElement
|
||||
className='raised button-alt headerHelpButton'
|
||||
title='Help'
|
||||
url='https://docs.jellyfin.org/general/server/users/adding-managing-users.html'
|
||||
/>
|
||||
</div>
|
||||
<div className='localUsers itemsContainer vertical-wrap'>
|
||||
{users.map(user => {
|
||||
return <UserCardBox key={user.Id} user={user} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserProfilesPage;
|
Loading…
Add table
Add a link
Reference in a new issue