1
0
Fork 0
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:
Bill Thornton 2021-10-17 01:27:45 -04:00 committed by GitHub
commit 5c8122a9dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 323 additions and 200 deletions

View 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;

View 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;

View 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;

View 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;