feat(user-avatar): users can now edit there image (image is taken from OIDC picture)

This commit is contained in:
Edouard Vanbelle
2026-05-26 00:50:38 +02:00
parent 8b6c8e345b
commit b0c5e7827e
43 changed files with 1563 additions and 250 deletions
+3 -12
View File
@@ -7,6 +7,7 @@ import { loadFiles } from './filesView.js';
import { updateStorageUsageDisplay } from './main.js';
import { app } from './state.js';
import { ui } from './ui.js';
import { updateUserMenuData } from './userMenu.js';
/**
* @import {User} from '../core/types.js'
@@ -96,14 +97,7 @@ async function checkAuthentication() {
const userData = JSON.parse(localStorage.getItem(USER_DATA_KEY) || '{}');
if (userData.username) {
// We have cached user data — render immediately, refresh in background
const userInitials = userData.username.substring(0, 2).toUpperCase();
document.querySelectorAll('.user-avatar, .user-menu-avatar').forEach((el) => {
el.textContent = userInitials;
});
const menuName = document.getElementById('user-menu-name');
const menuEmail = document.getElementById('user-menu-email');
if (menuName) menuName.textContent = userData.username;
if (menuEmail) menuEmail.textContent = userData.email || '';
updateUserMenuData();
updateStorageUsageDisplay(userData);
@@ -145,10 +139,7 @@ async function checkAuthentication() {
try {
const freshData = await refreshUserData();
if (freshData?.username) {
const userInitials = freshData.username.substring(0, 2).toUpperCase();
document.querySelectorAll('.user-avatar, .user-menu-avatar').forEach((el) => {
el.textContent = userInitials;
});
updateUserMenuData();
updateStorageUsageDisplay(freshData);
resolveHomeFolder().then(() => loadFiles());
} else {
+1 -1
View File
@@ -481,7 +481,7 @@ const ui = {
const id = cell.dataset.ownerId;
cell.dataset.ownerResolved = '1';
if (!id) continue;
cell.replaceChildren(createUserVignette(id, 'sm'));
cell.replaceChildren(createUserVignette(id, 'list'));
}
},
+33 -8
View File
@@ -2,6 +2,7 @@
* User menu, profile modal and logout logic
*/
import { createUserVignette } from '../components/userVignette.js';
import { getCsrfHeaders } from '../core/csrf.js';
import { formatFileSize, formatQuotaSize } from '../core/formatters.js';
import { i18n } from '../core/i18n.js';
@@ -21,6 +22,9 @@ function setupUserMenu() {
if (!wrapper || !avatarBtn || !menu) return;
// Populate avatar and name immediately from localStorage on every page load.
updateUserMenuData();
avatarBtn.addEventListener('click', (e) => {
e.stopPropagation();
const isOpen = wrapper.classList.contains('open');
@@ -156,20 +160,41 @@ function setupUserMenu() {
fetchAppVersion();
}
/**
* Mount avatar-only vignettes for the toolbar button and the dropdown header.
* Called whenever user data in localStorage changes (login, photo save, etc.).
*
* The toolbar button (#user-avatar-btn) and the menu header (.user-menu-header)
* are the stable mount points. Both receive a fresh vignette each call so
* the photo / initials are always in sync with the current localStorage state.
*
* @param {string} userId
*/
function _mountAvatarVignettes(userId) {
const avatarBtn = document.getElementById('user-avatar-btn');
if (avatarBtn) {
avatarBtn.replaceChildren(createUserVignette(userId, 'menu', { showName: false }));
}
const menuHeader = document.querySelector('.user-menu-header');
if (menuHeader) {
menuHeader.replaceChildren(createUserVignette(userId, 'xl', { showName: true, showEmail: true }));
}
}
/**
* @returns {void}
*/
function updateUserMenuData() {
const USER_DATA_KEY = 'oxicloud_user';
/** @type {import('../core/types.js').User} */
const userData = JSON.parse(localStorage.getItem(USER_DATA_KEY) || '{}');
const nameEl = document.getElementById('user-menu-name');
const emailEl = document.getElementById('user-menu-email');
const avatarEl = document.getElementById('user-menu-avatar');
const storageFill = document.getElementById('user-menu-storage-fill');
const storageText = document.getElementById('user-menu-storage-text');
if (userData.username) {
if (nameEl) nameEl.textContent = userData.username;
if (emailEl) emailEl.textContent = userData.email || '';
if (avatarEl) avatarEl.textContent = userData.username.substring(0, 2).toUpperCase();
if (userData.username && userData.id) {
_mountAvatarVignettes(userData.id);
}
const usedBytes = userData.storage_used_bytes || 0;
@@ -287,4 +312,4 @@ async function logout() {
window.location.href = '/login';
}
export { logout, setupUserMenu, showUserProfileModal };
export { logout, setupUserMenu, showUserProfileModal, updateUserMenuData };