diff --git a/src/components/dashboard/users/UserPasswordForm.tsx b/src/components/dashboard/users/UserPasswordForm.tsx new file mode 100644 index 0000000000..4ccfd28ce0 --- /dev/null +++ b/src/components/dashboard/users/UserPasswordForm.tsx @@ -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 = ({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 ( +
+
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+
+ {globalize.translate('HeaderEasyPinCode')} +
+
+
+ {globalize.translate('EasyPasswordHelp')} +
+
+
+ +
+
+
+ +
+ {globalize.translate('LabelInNetworkSignInWithEasyPasswordHelp')} +
+
+
+ + +
+
+
+
+ ); +}; + +export default UserPasswordForm; diff --git a/src/components/pages/UserImagePage.tsx b/src/components/pages/UserImagePage.tsx new file mode 100644 index 0000000000..51c00ed00d --- /dev/null +++ b/src/components/pages/UserImagePage.tsx @@ -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 = ({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 ( +
+
+
+
+ +
+
+
+

+ {userName} +

+
+ + +
+
+ +
+
+ ); +}; + +export default UserImagePage; diff --git a/src/components/pages/UserPasswordPage.tsx b/src/components/pages/UserPasswordPage.tsx new file mode 100644 index 0000000000..68b1108b7c --- /dev/null +++ b/src/components/pages/UserPasswordPage.tsx @@ -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 ( +
+
+
+
+

+ {userName} +

+ +
+
+ +
+ +
+
+
+ ); +}; + +export default UserPasswordPage; diff --git a/src/controllers/dashboard/users/userpassword.html b/src/controllers/dashboard/users/userpassword.html index 285533cc4c..984fcc2c9c 100644 --- a/src/controllers/dashboard/users/userpassword.html +++ b/src/controllers/dashboard/users/userpassword.html @@ -1,72 +1,3 @@
-
-
-
-
-

- ${Help} -
-
- - -
-
-
-
- -
-
- -
-
- -
-
-
- - -
-
-
-
-
-
-
- ${HeaderEasyPinCode} -
-
-
${EasyPasswordHelp}
-
-
- -
-
-
- -
${LabelInNetworkSignInWithEasyPasswordHelp}
-
-
- - -
-
-
-
-
-
diff --git a/src/controllers/dashboard/users/userpasswordpage.js b/src/controllers/dashboard/users/userpasswordpage.js deleted file mode 100644 index fa9c95ce20..0000000000 --- a/src/controllers/dashboard/users/userpasswordpage.js +++ /dev/null @@ -1,182 +0,0 @@ -import loading from '../../../components/loading/loading'; -import libraryMenu from '../../../scripts/libraryMenu'; -import globalize from '../../../scripts/globalize'; -import '../../../elements/emby-button/emby-button'; -import Dashboard from '../../../scripts/clientUtils'; -import toast from '../../../components/toast/toast'; -import confirm from '../../../components/confirm/confirm'; - -/* eslint-disable indent */ - - function loadUser(page, params) { - const userid = params.userId; - ApiClient.getUser(userid).then(function (user) { - Dashboard.getCurrentUser().then(function (loggedInUser) { - libraryMenu.setTitle(user.Name); - page.querySelector('.username').innerText = user.Name; - let showPasswordSection = true; - let showLocalAccessSection = false; - - if (user.ConnectLinkType == 'Guest') { - page.querySelector('.localAccessSection').classList.add('hide'); - showPasswordSection = false; - } else if (user.HasConfiguredPassword) { - page.querySelector('#btnResetPassword').classList.remove('hide'); - page.querySelector('#fldCurrentPassword').classList.remove('hide'); - showLocalAccessSection = true; - } else { - page.querySelector('#btnResetPassword').classList.add('hide'); - page.querySelector('#fldCurrentPassword').classList.add('hide'); - } - - if (showPasswordSection && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) { - page.querySelector('.passwordSection').classList.remove('hide'); - } else { - page.querySelector('.passwordSection').classList.add('hide'); - } - - if (showLocalAccessSection && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) { - page.querySelector('.localAccessSection').classList.remove('hide'); - } else { - page.querySelector('.localAccessSection').classList.add('hide'); - } - - const txtEasyPassword = page.querySelector('#txtEasyPassword'); - txtEasyPassword.value = ''; - - if (user.HasConfiguredEasyPassword) { - txtEasyPassword.placeholder = '******'; - page.querySelector('#btnResetEasyPassword').classList.remove('hide'); - } else { - txtEasyPassword.removeAttribute('placeholder'); - txtEasyPassword.placeholder = ''; - page.querySelector('#btnResetEasyPassword').classList.add('hide'); - } - - page.querySelector('.chkEnableLocalEasyPassword').checked = user.Configuration.EnableLocalPassword; - - import('../../../components/autoFocuser').then(({default: autoFocuser}) => { - autoFocuser.autoFocus(page); - }); - }); - }); - page.querySelector('#txtCurrentPassword').value = ''; - page.querySelector('#txtNewPassword').value = ''; - page.querySelector('#txtNewPasswordConfirm').value = ''; - } - - export default function (view, params) { - function saveEasyPassword() { - const userId = params.userId; - const easyPassword = view.querySelector('#txtEasyPassword').value; - - if (easyPassword) { - ApiClient.updateEasyPassword(userId, easyPassword).then(function () { - onEasyPasswordSaved(userId); - }); - } else { - onEasyPasswordSaved(userId); - } - } - - function onEasyPasswordSaved(userId) { - ApiClient.getUser(userId).then(function (user) { - user.Configuration.EnableLocalPassword = view.querySelector('.chkEnableLocalEasyPassword').checked; - ApiClient.updateUserConfiguration(user.Id, user.Configuration).then(function () { - loading.hide(); - toast(globalize.translate('SettingsSaved')); - - loadUser(view, params); - }); - }); - } - - function savePassword() { - const userId = params.userId; - let currentPassword = view.querySelector('#txtCurrentPassword').value; - const newPassword = view.querySelector('#txtNewPassword').value; - - if (view.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 = ''; - } - - ApiClient.updateUserPassword(userId, currentPassword, newPassword).then(function () { - loading.hide(); - toast(globalize.translate('PasswordSaved')); - - loadUser(view, params); - }, function () { - loading.hide(); - Dashboard.alert({ - title: globalize.translate('HeaderLoginFailure'), - message: globalize.translate('MessageInvalidUser') - }); - }); - } - - function onSubmit(e) { - const form = this; - - if (form.querySelector('#txtNewPassword').value != form.querySelector('#txtNewPasswordConfirm').value) { - toast(globalize.translate('PasswordMatchError')); - } else { - loading.show(); - savePassword(); - } - - e.preventDefault(); - return false; - } - - function onLocalAccessSubmit(e) { - loading.show(); - saveEasyPassword(); - e.preventDefault(); - return false; - } - - function resetPassword() { - const msg = globalize.translate('PasswordResetConfirmation'); - confirm(msg, globalize.translate('ResetPassword')).then(function () { - const userId = params.userId; - loading.show(); - ApiClient.resetUserPassword(userId).then(function () { - loading.hide(); - Dashboard.alert({ - message: globalize.translate('PasswordResetComplete'), - title: globalize.translate('ResetPassword') - }); - loadUser(view, params); - }); - }); - } - - function resetEasyPassword() { - const msg = globalize.translate('PinCodeResetConfirmation'); - - confirm(msg, globalize.translate('HeaderPinCodeReset')).then(function () { - const userId = params.userId; - loading.show(); - ApiClient.resetEasyPassword(userId).then(function () { - loading.hide(); - Dashboard.alert({ - message: globalize.translate('PinCodeResetComplete'), - title: globalize.translate('HeaderPinCodeReset') - }); - loadUser(view, params); - }); - }); - } - - view.querySelector('.updatePasswordForm').addEventListener('submit', onSubmit); - view.querySelector('.localAccessForm').addEventListener('submit', onLocalAccessSubmit); - view.querySelector('#btnResetEasyPassword').addEventListener('click', resetEasyPassword); - view.querySelector('#btnResetPassword').addEventListener('click', resetPassword); - view.addEventListener('viewshow', function () { - loadUser(view, params); - }); - } - -/* eslint-enable indent */ diff --git a/src/controllers/user/profile/index.html b/src/controllers/user/profile/index.html index 3eaa2f7299..91c3f7d7d9 100644 --- a/src/controllers/user/profile/index.html +++ b/src/controllers/user/profile/index.html @@ -1,69 +1,3 @@
-
-
-
- -
-
-
-

-
- - -
-
-
-
-

- ${HeaderPassword} -

-
- -
-
- -
-
- -
-
- - -
-
-
-
-
-

${HeaderEasyPinCode}

-
${EasyPasswordHelp}
-
-
- -
-
- -
${LabelInNetworkSignInWithEasyPasswordHelp}
-
-
- - -
-
-
-
+
diff --git a/src/controllers/user/profile/index.js b/src/controllers/user/profile/index.js deleted file mode 100644 index c6d23eb2f9..0000000000 --- a/src/controllers/user/profile/index.js +++ /dev/null @@ -1,104 +0,0 @@ -import UserPasswordPage from '../../dashboard/users/userpasswordpage'; -import loading from '../../../components/loading/loading'; -import libraryMenu from '../../../scripts/libraryMenu'; -import { appHost } from '../../../components/apphost'; -import globalize from '../../../scripts/globalize'; -import '../../../elements/emby-button/emby-button'; -import Dashboard from '../../../scripts/clientUtils'; -import toast from '../../../components/toast/toast'; -import confirm from '../../../components/confirm/confirm'; - -function reloadUser(page) { - const userId = getParameterByName('userId'); - loading.show(); - ApiClient.getUser(userId).then(function (user) { - page.querySelector('.username').innerText = user.Name; - libraryMenu.setTitle(user.Name); - - let imageUrl = 'assets/img/avatar.png'; - if (user.PrimaryImageTag) { - imageUrl = ApiClient.getUserImageUrl(user.Id, { - tag: user.PrimaryImageTag, - type: 'Primary' - }); - } - - const userImage = page.querySelector('#image'); - userImage.style.backgroundImage = 'url(' + imageUrl + ')'; - - Dashboard.getCurrentUser().then(function (loggedInUser) { - if (user.PrimaryImageTag) { - page.querySelector('#btnAddImage').classList.add('hide'); - page.querySelector('#btnDeleteImage').classList.remove('hide'); - } else if (appHost.supports('fileinput') && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) { - page.querySelector('#btnDeleteImage').classList.add('hide'); - page.querySelector('#btnAddImage').classList.remove('hide'); - } - }); - loading.hide(); - }); -} - -function 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')); - } -} - -function onFileReaderAbort() { - loading.hide(); - toast(globalize.translate('FileReadCancelled')); -} - -function setFiles(page, files) { - const userImage = page.querySelector('#image'); - const file = files[0]; - - if (!file || !file.type.match('image.*')) { - return false; - } - - const reader = new FileReader(); - reader.onerror = onFileReaderError; - reader.onabort = onFileReaderAbort; - reader.onload = function (evt) { - userImage.style.backgroundImage = 'url(' + evt.target.result + ')'; - const userId = getParameterByName('userId'); - ApiClient.uploadUserImage(userId, 'Primary', file).then(function () { - loading.hide(); - reloadUser(page); - }); - }; - - reader.readAsDataURL(file); -} - -export default function (view, params) { - reloadUser(view); - new UserPasswordPage(view, params); - view.querySelector('#btnDeleteImage').addEventListener('click', function () { - confirm(globalize.translate('DeleteImageConfirmation'), globalize.translate('DeleteImage')).then(function () { - loading.show(); - const userId = getParameterByName('userId'); - ApiClient.deleteUserImage(userId, 'primary').then(function () { - loading.hide(); - reloadUser(view); - }); - }); - }); - view.querySelector('#btnAddImage').addEventListener('click', function () { - view.querySelector('#uploadImage').click(); - }); - view.querySelector('#uploadImage').addEventListener('change', function (evt) { - setFiles(view, evt.target.files); - }); -} diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 538c0c03ef..f2e0507521 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -81,7 +81,7 @@ import { appRouter } from '../components/appRouter'; alias: '/myprofile.html', path: 'user/profile/index.html', autoFocus: false, - controller: 'user/profile/index' + pageComponent: 'UserImagePage' }); defineRoute({ @@ -471,7 +471,7 @@ import { appRouter } from '../components/appRouter'; alias: '/userpassword.html', path: 'dashboard/users/userpassword.html', autoFocus: false, - controller: 'dashboard/users/userpasswordpage' + pageComponent: 'UserPasswordPage' }); defineRoute({