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

Convert userPasswordPage & UserImagePage to react

This commit is contained in:
grafixeyehero 2022-01-05 20:35:58 +03:00 committed by Bill Thornton
parent 4a8806e1f6
commit 2aa41f8a33
8 changed files with 498 additions and 424 deletions

View file

@ -0,0 +1,283 @@
import React, { FunctionComponent, useEffect, useRef } from 'react';
import Dashboard from '../../../scripts/clientUtils';
import globalize from '../../../scripts/globalize';
import LibraryMenu from '../../../scripts/libraryMenu';
import confirm from '../../confirm/confirm';
import loading from '../../loading/loading';
import toast from '../../toast/toast';
import ButtonElement from './ButtonElement';
import CheckBoxElement from './CheckBoxElement';
import InputElement from './InputElement';
type IProps = {
userId?: string;
}
const UserPasswordForm: FunctionComponent<IProps> = ({userId}: IProps) => {
const element = useRef(null);
const loadUser = (Id) => {
window.ApiClient.getUser(Id).then(function (user) {
Dashboard.getCurrentUser().then(function (loggedInUser) {
LibraryMenu.setTitle(user.Name);
let showPasswordSection = true;
let showLocalAccessSection = false;
if (user.ConnectLinkType == 'Guest') {
element.current?.querySelector('.localAccessSection').classList.add('hide');
showPasswordSection = false;
} else if (user.HasConfiguredPassword) {
element.current?.querySelector('.btnResetPassword').classList.remove('hide');
element.current?.querySelector('#fldCurrentPassword').classList.remove('hide');
showLocalAccessSection = true;
} else {
element.current?.querySelector('.btnResetPassword').classList.add('hide');
element.current?.querySelector('#fldCurrentPassword').classList.add('hide');
}
if (showPasswordSection && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) {
element.current?.querySelector('.passwordSection').classList.remove('hide');
} else {
element.current?.querySelector('.passwordSection').classList.add('hide');
}
if (showLocalAccessSection && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) {
element.current?.querySelector('.localAccessSection').classList.remove('hide');
} else {
element.current?.querySelector('.localAccessSection').classList.add('hide');
}
const txtEasyPassword = element.current?.querySelector('#txtEasyPassword');
txtEasyPassword.value = '';
if (user.HasConfiguredEasyPassword) {
txtEasyPassword.placeholder = '******';
element.current?.querySelector('.btnResetEasyPassword').classList.remove('hide');
} else {
txtEasyPassword.removeAttribute('placeholder');
txtEasyPassword.placeholder = '';
element.current?.querySelector('.btnResetEasyPassword').classList.add('hide');
}
element.current.querySelector('.chkEnableLocalEasyPassword').checked = user.Configuration.EnableLocalPassword;
import('../../autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(element.current);
});
});
});
element.current.querySelector('#txtCurrentPassword').value = '';
element.current.querySelector('#txtNewPassword').value = '';
element.current.querySelector('#txtNewPasswordConfirm').value = '';
};
useEffect(() => {
loadUser(userId);
const onSubmit = (e) => {
const form = element.current;
if (form.querySelector('#txtNewPassword').value != form.querySelector('#txtNewPasswordConfirm').value) {
toast(globalize.translate('PasswordMatchError'));
} else {
loading.show();
savePassword();
}
e.preventDefault();
return false;
};
const savePassword = () => {
let currentPassword = element.current?.querySelector('#txtCurrentPassword').value;
const newPassword = element.current?.querySelector('#txtNewPassword').value;
if (element.current?.querySelector('#fldCurrentPassword').classList.contains('hide')) {
// Firefox does not respect autocomplete=off, so clear it if the field is supposed to be hidden (and blank)
// This should only happen when user.HasConfiguredPassword is false, but this information is not passed on
currentPassword = '';
}
window.ApiClient.updateUserPassword(userId, currentPassword, newPassword).then(function () {
loading.hide();
toast(globalize.translate('PasswordSaved'));
loadUser(userId);
}, function () {
loading.hide();
Dashboard.alert({
title: globalize.translate('HeaderLoginFailure'),
message: globalize.translate('MessageInvalidUser')
});
});
};
const onLocalAccessSubmit = (e) => {
loading.show();
saveEasyPassword();
e.preventDefault();
return false;
};
const saveEasyPassword = () => {
const easyPassword = element.current?.querySelector('#txtEasyPassword').value;
if (easyPassword) {
window.ApiClient.updateEasyPassword(userId, easyPassword).then(function () {
onEasyPasswordSaved(userId);
});
} else {
onEasyPasswordSaved(userId);
}
};
const onEasyPasswordSaved = (Id) => {
window.ApiClient.getUser(Id).then(function (user) {
user.Configuration.EnableLocalPassword = element.current?.querySelector('.chkEnableLocalEasyPassword').checked;
window.ApiClient.updateUserConfiguration(user.Id, user.Configuration).then(function () {
loading.hide();
toast(globalize.translate('SettingsSaved'));
loadUser(userId);
});
});
};
const resetEasyPassword = () => {
const msg = globalize.translate('PinCodeResetConfirmation');
confirm(msg, globalize.translate('HeaderPinCodeReset')).then(function () {
loading.show();
window.ApiClient.resetEasyPassword(userId).then(function () {
loading.hide();
Dashboard.alert({
message: globalize.translate('PinCodeResetComplete'),
title: globalize.translate('HeaderPinCodeReset')
});
loadUser(userId);
});
});
};
const resetPassword = () => {
const msg = globalize.translate('PasswordResetConfirmation');
confirm(msg, globalize.translate('ResetPassword')).then(function () {
loading.show();
window.ApiClient.resetUserPassword(userId).then(function () {
loading.hide();
Dashboard.alert({
message: globalize.translate('PasswordResetComplete'),
title: globalize.translate('ResetPassword')
});
loadUser(userId);
});
});
};
element?.current?.querySelector('.updatePasswordForm').addEventListener('submit', onSubmit);
element?.current?.querySelector('.localAccessForm').addEventListener('submit', onLocalAccessSubmit);
element?.current?.querySelector('.btnResetEasyPassword').addEventListener('click', resetEasyPassword);
element?.current?.querySelector('.btnResetPassword').addEventListener('click', resetPassword);
}, [userId]);
return (
<div ref={element}>
<form
className='updatePasswordForm passwordSection hide'
style={{margin: '0 auto 2em'}}
>
<div className='detailSection'>
<div id='fldCurrentPassword' className='inputContainer hide'>
<InputElement
type='password'
id='txtCurrentPassword'
label='LabelCurrentPassword'
options={'autoComplete="off"'}
/>
</div>
<div className='inputContainer'>
<InputElement
type='password'
id='txtNewPassword'
label='LabelNewPassword'
options={'autoComplete="off"'}
/>
</div>
<div className='inputContainer'>
<InputElement
type='password'
id='txtNewPasswordConfirm'
label='LabelNewPasswordConfirm'
options={'autoComplete="off"'}
/>
</div>
<br />
<div>
<ButtonElement
type='submit'
className='raised button-submit block'
title='Save'
/>
<ButtonElement
type='button'
className='raised btnResetPassword button-cancel block hide'
title='ResetPassword'
/>
</div>
</div>
</form>
<br />
<form
className='localAccessForm localAccessSection'
style={{margin: '0 auto'}}
>
<div className='detailSection'>
<div className='detailSectionHeader'>
{globalize.translate('HeaderEasyPinCode')}
</div>
<br />
<div>
{globalize.translate('EasyPasswordHelp')}
</div>
<br />
<div className='inputContainer'>
<InputElement
type='number'
id='txtEasyPassword'
label='LabelEasyPinCode'
options={'autoComplete="off" pattern="[0-9]*" step="1" maxlength="5"'}
/>
</div>
<br />
<div className='checkboxContainer checkboxContainer-withDescription'>
<CheckBoxElement
type='checkbox'
className='chkEnableLocalEasyPassword'
title='LabelInNetworkSignInWithEasyPassword'
/>
<div className='fieldDescription checkboxFieldDescription'>
{globalize.translate('LabelInNetworkSignInWithEasyPasswordHelp')}
</div>
</div>
<div>
<ButtonElement
type='submit'
className='raised button-submit block'
title='Save'
/>
<ButtonElement
type='button'
className='raised btnResetEasyPassword button-cancel block hide'
title='ButtonResetEasyPassword'
/>
</div>
</div>
</form>
</div>
);
};
export default UserPasswordForm;

View file

@ -0,0 +1,165 @@
import React, { FunctionComponent, useEffect, useState, useRef } from 'react';
import Dashboard from '../../scripts/clientUtils';
import globalize from '../../scripts/globalize';
import LibraryMenu from '../../scripts/libraryMenu';
import { appHost } from '../apphost';
import confirm from '../confirm/confirm';
import ButtonElement from '../dashboard/users/ButtonElement';
import UserPasswordForm from '../dashboard/users/UserPasswordForm';
import loading from '../loading/loading';
import toast from '../toast/toast';
type IProps = {
userId?: string;
}
const UserImagePage: FunctionComponent<IProps> = ({userId}: IProps) => {
const [ userName, setUserName ] = useState('');
const element = useRef(null);
const reloadUser = (Id) => {
loading.show();
window.ApiClient.getUser(Id).then(function (user) {
setUserName(user.Name);
LibraryMenu.setTitle(user.Name);
let imageUrl = 'assets/img/avatar.png';
if (user.PrimaryImageTag) {
imageUrl = window.ApiClient.getUserImageUrl(user.Id, {
tag: user.PrimaryImageTag,
type: 'Primary'
});
}
const userImage = element.current?.querySelector('#image');
userImage.style.backgroundImage = 'url(' + imageUrl + ')';
Dashboard.getCurrentUser().then(function (loggedInUser) {
if (user.PrimaryImageTag) {
element.current?.querySelector('.btnAddImage').classList.add('hide');
element.current?.querySelector('.btnDeleteImage').classList.remove('hide');
} else if (appHost.supports('fileinput') && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) {
element.current?.querySelector('.btnDeleteImage').classList.add('hide');
element.current?.querySelector('.btnAddImage').classList.remove('hide');
}
});
loading.hide();
});
};
useEffect(() => {
reloadUser(userId);
const onFileReaderError = (evt) => {
loading.hide();
switch (evt.target.error.code) {
case evt.target.error.NOT_FOUND_ERR:
toast(globalize.translate('FileNotFound'));
break;
case evt.target.error.ABORT_ERR:
onFileReaderAbort();
break;
case evt.target.error.NOT_READABLE_ERR:
default:
toast(globalize.translate('FileReadError'));
}
};
const onFileReaderAbort = () => {
loading.hide();
toast(globalize.translate('FileReadCancelled'));
};
const setFiles = (evt) => {
const userImage = element?.current?.querySelector('#image');
const file = evt.target.files[0];
if (!file || !file.type.match('image.*')) {
return false;
}
const reader: FileReader = new FileReader();
reader.onerror = onFileReaderError;
reader.onabort = onFileReaderAbort;
reader.onload = () => {
userImage.style.backgroundImage = 'url(' + reader.result + ')';
window.ApiClient.uploadUserImage(userId, 'Primary', file).then(function () {
loading.hide();
reloadUser(userId);
});
};
reader.readAsDataURL(file);
};
element?.current?.querySelector('.btnDeleteImage').addEventListener('click', function () {
confirm(
globalize.translate('DeleteImageConfirmation'),
globalize.translate('DeleteImage')
).then(function () {
loading.show();
window.ApiClient.deleteUserImage(userId, 'primary').then(function () {
loading.hide();
reloadUser(userId);
});
});
});
element?.current?.querySelector('.btnAddImage').addEventListener('click', function () {
element?.current?.querySelector('#uploadImage').click();
});
element?.current?.querySelector('#uploadImage').addEventListener('change', function (evt) {
setFiles(evt);
});
}, [userId]);
return (
<div ref={element}>
<div className='padded-left padded-right padded-bottom-page'>
<div
className='readOnlyContent'
style={{margin: '0 auto', marginBottom: '1.8em', padding: '0 1em', display: 'flex', flexDirection: 'row', alignItems: 'center'}}
>
<div
style={{position: 'relative', display: 'inline-block', maxWidth: 200 }}
>
<input
id='uploadImage'
type='file'
accept='image/*'
style={{position: 'absolute', right: 0, width: '100%', height: '100%', opacity: 0, cursor: 'pointer'}}
/>
<div
id='image'
style={{width: 200, height: 200, backgroundRepeat: 'no-repeat', backgroundPosition: 'center', borderRadius: '100%', backgroundSize: 'cover'}}
/>
</div>
<div style={{verticalAlign: 'top', margin: '1em 2em', display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
<h2 className='username' style={{margin: 0, fontSize: 'xx-large'}}>
{userName}
</h2>
<br />
<ButtonElement
type='button'
className='raised btnAddImage hide'
title='ButtonAddImage'
/>
<ButtonElement
type='button'
className='raised btnDeleteImage hide'
title='DeleteImage'
/>
</div>
</div>
<UserPasswordForm
userId={userId}
/>
</div>
</div>
);
};
export default UserImagePage;

View file

@ -0,0 +1,47 @@
import React, { FunctionComponent, useEffect, useState } from 'react';
import { appRouter } from '../appRouter';
import SectionTitleLinkElement from '../dashboard/users/SectionTitleLinkElement';
import SectionTabs from '../dashboard/users/SectionTabs';
import UserPasswordForm from '../dashboard/users/UserPasswordForm';
const UserPasswordPage: FunctionComponent = () => {
const userId = appRouter.param('userId');
const [ userName, setUserName ] = useState('');
const loadUser = (Id) => {
window.ApiClient.getUser(Id).then(function (user) {
setUserName(user.Name);
});
};
useEffect(() => {
loadUser(userId);
}, [userId]);
return (
<div>
<div className='content-primary'>
<div className='verticalSection'>
<div className='sectionTitleContainer flex align-items-center'>
<h2 className='sectionTitle username'>
{userName}
</h2>
<SectionTitleLinkElement
className='raised button-alt headerHelpButton'
title='Help'
url='https://docs.jellyfin.org/general/server/users/'
/>
</div>
</div>
<SectionTabs activeTab='userpassword'/>
<div className='readOnlyContent'>
<UserPasswordForm
userId={userId}
/>
</div>
</div>
</div>
);
};
export default UserPasswordPage;