diff --git a/src/components/DashboardComponent/users/UserProfilesPage/SectionTitleButtonElement.tsx b/src/components/DashboardComponent/users/UserProfilesPage/SectionTitleButtonElement.tsx new file mode 100644 index 0000000000..fe384e3813 --- /dev/null +++ b/src/components/DashboardComponent/users/UserProfilesPage/SectionTitleButtonElement.tsx @@ -0,0 +1,33 @@ +import React, { FunctionComponent } from 'react'; +import globalize from '../../../../scripts/globalize'; + +const createButtonElement = ({ className, title, icon }) => ({ + __html: `` +}); + +type IProps = { + title?: string; + className?: string; + icon?: string, +} + +const SectionTitleButtonElement: FunctionComponent = ({ className, title, icon }: IProps) => { + return ( +
+ ); +}; + +export default SectionTitleButtonElement; diff --git a/src/components/DashboardComponent/users/UserProfilesPage/SectionTitleLinkElement.tsx b/src/components/DashboardComponent/users/UserProfilesPage/SectionTitleLinkElement.tsx new file mode 100644 index 0000000000..7c02802e2e --- /dev/null +++ b/src/components/DashboardComponent/users/UserProfilesPage/SectionTitleLinkElement.tsx @@ -0,0 +1,34 @@ +import React, { FunctionComponent } from 'react'; +import globalize from '../../../../scripts/globalize'; + +const createLinkElement = ({ className, href, title }) => ({ + __html: ` + ${title} + ` +}); + +type IProps = { + title?: string; + className?: string; + url?: string +} + +const SectionTitleLinkElement: FunctionComponent = ({ className, title, url }: IProps) => { + return ( +
+ ); +}; + +export default SectionTitleLinkElement; diff --git a/src/components/DashboardComponent/users/UserProfilesPage/UserCardBox.tsx b/src/components/DashboardComponent/users/UserProfilesPage/UserCardBox.tsx new file mode 100644 index 0000000000..ccbb41c14f --- /dev/null +++ b/src/components/DashboardComponent/users/UserProfilesPage/UserCardBox.tsx @@ -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: ` + ${renderImgUrl} + ` +}); + +const createButtonElement = () => ({ + __html: `` +}); + +type IProps = { + user?: Record; +} + +const getLastSeenText = (lastActivityDate) => { + if (lastActivityDate) { + return globalize.translate('LastSeen', formatDistanceToNow(Date.parse(lastActivityDate), localeWithSuffix)); + } + + return ''; +}; + +const UserCardBox: FunctionComponent = ({ 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 ? + `
` : + `
+ +
`; + + return ( +
+
+
+
+
+
+
+
+
+ {user.Name} +
+
+
+
+ {lastSeen != '' ? lastSeen : ''} +
+
+
+
+ ); +}; + +export default UserCardBox; diff --git a/src/components/pages/UserProfilesPage.tsx b/src/components/pages/UserProfilesPage.tsx new file mode 100644 index 0000000000..fd1641f900 --- /dev/null +++ b/src/components/pages/UserProfilesPage.tsx @@ -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 '../DashboardComponent/users/UserProfilesPage/UserCardBox'; +import SectionTitleButtonElement from '../DashboardComponent/users/UserProfilesPage/SectionTitleButtonElement'; +import SectionTitleLinkElement from '../DashboardComponent/users/UserProfilesPage/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 ( +
+
+
+
+

+ {globalize.translate('HeaderUsers')} +

+ + +
+
+ {users.map((user, index: number)=> { + return ; + })} +
+
+
+
+ ); +}; + +export default UserProfilesPage; diff --git a/src/controllers/dashboard/users/userprofiles.html b/src/controllers/dashboard/users/userprofiles.html index 0a72f40ed7..047cdff1fc 100644 --- a/src/controllers/dashboard/users/userprofiles.html +++ b/src/controllers/dashboard/users/userprofiles.html @@ -1,16 +1,3 @@
-
-
-
-
-

${HeaderUsers}

- - ${Help} -
-
-
-
-
+
diff --git a/src/controllers/dashboard/users/userprofilespage.js b/src/controllers/dashboard/users/userprofilespage.js deleted file mode 100644 index 7b91692600..0000000000 --- a/src/controllers/dashboard/users/userprofilespage.js +++ /dev/null @@ -1,185 +0,0 @@ -import loading from '../../../components/loading/loading'; -import dom from '../../../scripts/dom'; -import globalize from '../../../scripts/globalize'; -import { formatDistanceToNow } from 'date-fns'; -import { localeWithSuffix } from '../../../scripts/dfnshelper'; -import '../../../elements/emby-button/paper-icon-button-light'; -import '../../../components/cardbuilder/card.scss'; -import '../../../elements/emby-button/emby-button'; -import '../../../components/indicators/indicators.scss'; -import '../../../assets/css/flexstyles.scss'; -import Dashboard, { pageIdOn } from '../../../scripts/clientUtils'; -import confirm from '../../../components/confirm/confirm'; -import cardBuilder from '../../../components/cardbuilder/cardBuilder'; - -/* eslint-disable indent */ - - function deleteUser(page, id) { - const msg = globalize.translate('DeleteUserConfirmation'); - - confirm({ - title: globalize.translate('DeleteUser'), - text: msg, - confirmText: globalize.translate('Delete'), - primary: 'delete' - }).then(function () { - loading.show(); - ApiClient.deleteUser(id).then(function () { - loadData(page); - }); - }); - } - - function showUserMenu(elem) { - const card = dom.parentWithClass(elem, 'card'); - const page = dom.parentWithClass(card, 'page'); - const userId = card.getAttribute('data-userid'); - const menuItems = []; - 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(page, userId); - } - } - }); - }); - } - - function getUserHtml(user) { - let html = ''; - let cssClass = 'card squareCard scalableCard squareCard-scalable'; - - if (user.Policy.IsDisabled) { - cssClass += ' grayscale'; - } - - html += "
"; - html += '
'; - html += '
'; - html += '
'; - html += ``; - let imgUrl; - - if (user.PrimaryImageTag) { - imgUrl = ApiClient.getUserImageUrl(user.Id, { - width: 300, - tag: user.PrimaryImageTag, - type: 'Primary' - }); - } - - let imageClass = 'cardImage'; - - if (user.Policy.IsDisabled) { - imageClass += ' disabledUser'; - } - - if (imgUrl) { - html += ''; - html += '
'; - html += '
'; - html += '
'; - html += user.Name; - html += '
'; - html += ''; - html += '
'; - html += '
'; - const lastSeen = getLastSeenText(user.LastActivityDate); - html += lastSeen != '' ? lastSeen : ' '; - html += '
'; - html += '
'; - html += '
'; - return html + '
'; - } - // FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix - // how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences - function getLastSeenText(lastActivityDate) { - if (lastActivityDate) { - return globalize.translate('LastSeen', formatDistanceToNow(Date.parse(lastActivityDate), localeWithSuffix)); - } - - return ''; - } - - function getUserSectionHtml(users) { - return users.map(function (u__q) { - return getUserHtml(u__q); - }).join(''); - } - - function renderUsers(page, users) { - page.querySelector('.localUsers').innerHTML = getUserSectionHtml(users); - } - - function loadData(page) { - loading.show(); - ApiClient.getUsers().then(function (users) { - renderUsers(page, users); - loading.hide(); - }); - } - - pageIdOn('pageinit', 'userProfilesPage', function () { - const page = this; - page.querySelector('.btnAddUser').addEventListener('click', function() { - Dashboard.navigate('usernew.html'); - }); - page.querySelector('.localUsers').addEventListener('click', function (e__e) { - const btnUserMenu = dom.parentWithClass(e__e.target, 'btnUserMenu'); - - if (btnUserMenu) { - showUserMenu(btnUserMenu); - } - }); - }); - - pageIdOn('pagebeforeshow', 'userProfilesPage', function () { - loadData(this); - }); - -/* eslint-enable indent */ diff --git a/src/scripts/routes.js b/src/scripts/routes.js index c747957248..ed0b40b97d 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -479,7 +479,7 @@ import { appRouter } from '../components/appRouter'; path: 'dashboard/users/userprofiles.html', autoFocus: false, roles: 'admin', - controller: 'dashboard/users/userprofilespage' + pageComponent: 'UserProfilesPage' }); defineRoute({