1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00
This commit is contained in:
David Murdoch 2025-03-30 11:01:14 -04:00 committed by GitHub
commit ae5fcf9c50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 59 additions and 8 deletions

View file

@ -45,6 +45,11 @@ const AppUserMenu: FC<AppUserMenuProps> = ({
onMenuClose(); onMenuClose();
}, [ onMenuClose ]); }, [ onMenuClose ]);
const onSwitchUserClick = useCallback(() => {
Dashboard.navigate('login.html')
onMenuClose();
}, [ onMenuClose ]);
const onLogoutClick = useCallback(() => { const onLogoutClick = useCallback(() => {
Dashboard.logout(); Dashboard.logout();
onMenuClose(); onMenuClose();
@ -168,6 +173,17 @@ const AppUserMenu: FC<AppUserMenuProps> = ({
</MenuItem> </MenuItem>
)} )}
<MenuItem
onClick={onSwitchUserClick}
>
<ListItemIcon>
<Logout />
</ListItemIcon>
<ListItemText>
{globalize.translate('SwitchUser')}
</ListItemText>
</MenuItem>
<MenuItem <MenuItem
onClick={onLogoutClick} onClick={onLogoutClick}
> >

View file

@ -33,6 +33,11 @@
<div id="divUsers" class="itemsContainer vertical-wrap centered"></div> <div id="divUsers" class="itemsContainer vertical-wrap centered"></div>
</div> </div>
<div class="visualLoginForm" style="text-align: center;">
<h1 style="margin-top:1em;">${HeaderWhosWatching}</h1>
<div id="divSignedInUsers" class="itemsContainer vertical-wrap centered"></div>
</div>
<div class="readOnlyContent" style="margin: .5em auto 1em;"> <div class="readOnlyContent" style="margin: .5em auto 1em;">
<button is="emby-button" type="button" class="raised cancel block btnManual"> <button is="emby-button" type="button" class="raised cancel block btnManual">
<span>${ButtonManualLogin}</span> <span>${ButtonManualLogin}</span>

View file

@ -26,6 +26,12 @@ function authenticateUserByName(page, apiClient, url, username, password) {
const user = result.User; const user = result.User;
loading.hide(); loading.hide();
// store this user/access token in local storage
// TODO: make this not suck so much
let users = JSON.parse(localStorage.getItem('users')) || [];
users = users.concat({PrimaryImageTag: user.PrimaryImageTag, HasPassword: user.HasPassword, Name: user.Name, Id: user.Id, AccessToken: result.AccessToken});
localStorage.setItem('users', JSON.stringify(users))
onLoginSuccessful(user.Id, result.AccessToken, apiClient, url); onLoginSuccessful(user.Id, result.AccessToken, apiClient, url);
}, function (response) { }, function (response) {
page.querySelector('#txtManualPassword').value = ''; page.querySelector('#txtManualPassword').value = '';
@ -132,7 +138,7 @@ function showManualForm(context, showCancel, focusPassword) {
} }
} }
function loadUserList(context, apiClient, users) { function loadUserList(context, apiClient, users, target) {
let html = ''; let html = '';
for (const user of users) { for (const user of users) {
@ -152,7 +158,7 @@ function loadUserList(context, apiClient, users) {
html += '<div class="' + cardBoxCssClass + '">'; html += '<div class="' + cardBoxCssClass + '">';
html += '<div class="cardScalable">'; html += '<div class="cardScalable">';
html += '<div class="cardPadder cardPadder-square"></div>'; html += '<div class="cardPadder cardPadder-square"></div>';
html += `<div class="cardContent" data-haspw="${user.HasPassword}" data-username="${user.Name}" data-userid="${user.Id}">`; html += `<div class="cardContent" data-accesstoken="${user.AccessToken}" data-haspw="${user.HasPassword}" data-username="${user.Name}" data-userid="${user.Id}">`;
let imgUrl; let imgUrl;
if (user.PrimaryImageTag) { if (user.PrimaryImageTag) {
@ -178,7 +184,7 @@ function loadUserList(context, apiClient, users) {
html += '</button>'; html += '</button>';
} }
context.querySelector('#divUsers').innerHTML = html; target.innerHTML = html;
} }
export default function (view, params) { export default function (view, params) {
@ -214,7 +220,7 @@ export default function (view, params) {
}); });
} }
view.querySelector('#divUsers').addEventListener('click', function (e) { view.querySelectorAll('#divUsers,#divSignedInUsers').forEach(el => el.addEventListener('click', function (e) {
const card = dom.parentWithClass(e.target, 'card'); const card = dom.parentWithClass(e.target, 'card');
const cardContent = card ? card.querySelector('.cardContent') : null; const cardContent = card ? card.querySelector('.cardContent') : null;
@ -223,8 +229,12 @@ export default function (view, params) {
const id = cardContent.getAttribute('data-userid'); const id = cardContent.getAttribute('data-userid');
const name = cardContent.getAttribute('data-username'); const name = cardContent.getAttribute('data-username');
const haspw = cardContent.getAttribute('data-haspw'); const haspw = cardContent.getAttribute('data-haspw');
const accessToken = cardContent.getAttribute('data-accesstoken');
if (id === 'manual') { // if the clicked card has an accessToken attached we can just log
// in right away...
if (accessToken) {
onLoginSuccessful(id, accessToken, getApiClient(), getTargetUrl());
} else if (id === 'manual') {
context.querySelector('#txtManualName').value = ''; context.querySelector('#txtManualName').value = '';
showManualForm(context, true); showManualForm(context, true);
} else if (haspw == 'false') { } else if (haspw == 'false') {
@ -235,7 +245,7 @@ export default function (view, params) {
showManualForm(context, true, true); showManualForm(context, true, true);
} }
} }
}); }));
view.querySelector('.manualLoginForm').addEventListener('submit', function (e) { view.querySelector('.manualLoginForm').addEventListener('submit', function (e) {
appSettings.enableAutoLogin(view.querySelector('.chkRememberLogin').checked); appSettings.enableAutoLogin(view.querySelector('.chkRememberLogin').checked);
authenticateUserByName(view, getApiClient(), getTargetUrl(), view.querySelector('#txtManualName').value, view.querySelector('#txtManualPassword').value); authenticateUserByName(view, getApiClient(), getTargetUrl(), view.querySelector('#txtManualName').value, view.querySelector('#txtManualPassword').value);
@ -278,10 +288,16 @@ export default function (view, params) {
console.debug('Failed to get QuickConnect status'); console.debug('Failed to get QuickConnect status');
}); });
// TODO: this shouldn't suck
const loggedInUsers = JSON.parse(window.localStorage.getItem('users') || "[]");
if (loggedInUsers && loggedInUsers.length) {
loadUserList(view, apiClient, loggedInUsers, view.querySelector('#divSignedInUsers'));
}
apiClient.getPublicUsers().then(function (users) { apiClient.getPublicUsers().then(function (users) {
if (users.length) { if (users.length) {
showVisualForm(); showVisualForm();
loadUserList(view, apiClient, users); loadUserList(view, apiClient, users, context.querySelector('#divUsers'));
} else { } else {
view.querySelector('#txtManualName').value = ''; view.querySelector('#txtManualName').value = '';
showManualForm(view, false, false); showManualForm(view, false, false);

View file

@ -104,6 +104,14 @@
</div> </div>
</div> </div>
</a> </a>
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="btnSwitchUser listItem-border">
<div class="listItem">
<span class="material-icons listItemIcon listItemIcon-transparent exit_to_app" aria-hidden="true"></span>
<div class="listItemBody">
<div class="listItemBodyText">${SwitchUser}</div>
</div>
</div>
</a>
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="btnLogout listItem-border"> <a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="btnLogout listItem-border">
<div class="listItem"> <div class="listItem">
<span class="material-icons listItemIcon listItemIcon-transparent exit_to_app" aria-hidden="true"></span> <span class="material-icons listItemIcon listItemIcon-transparent exit_to_app" aria-hidden="true"></span>

View file

@ -9,6 +9,10 @@ export default function (view, params) {
Dashboard.logout(); Dashboard.logout();
}); });
view.querySelector('.btnSwitchUser').addEventListener('click', function () {
Dashboard.navigate("login.html");
});
view.querySelector('.selectServer').addEventListener('click', function () { view.querySelector('.selectServer').addEventListener('click', function () {
Dashboard.selectServer(); Dashboard.selectServer();
}); });

View file

@ -132,6 +132,7 @@
"ButtonShutdown": "Shutdown", "ButtonShutdown": "Shutdown",
"ButtonSignIn": "Sign In", "ButtonSignIn": "Sign In",
"ButtonSignOut": "Sign Out", "ButtonSignOut": "Sign Out",
"ButtonSwitchUser": "Switch User",
"ButtonExitApp": "Exit Application", "ButtonExitApp": "Exit Application",
"ButtonSpace": "Space", "ButtonSpace": "Space",
"ButtonSplit": "Split", "ButtonSplit": "Split",
@ -485,6 +486,7 @@
"HeaderPlaybackError": "Playback Error", "HeaderPlaybackError": "Playback Error",
"HeaderPlayOn": "Play On", "HeaderPlayOn": "Play On",
"HeaderPleaseSignIn": "Please sign in", "HeaderPleaseSignIn": "Please sign in",
"HeaderWhosWatching": "Who's Watching",
"HeaderPortRanges": "Firewall and Proxy Settings", "HeaderPortRanges": "Firewall and Proxy Settings",
"HeaderPreferredMetadataLanguage": "Preferred Metadata Language", "HeaderPreferredMetadataLanguage": "Preferred Metadata Language",
"HeaderPreviewLyrics": "Preview Lyrics", "HeaderPreviewLyrics": "Preview Lyrics",