feat(list view): show the owner of the File or Folder in list view

This commit is contained in:
Edouard Vanbelle
2026-05-25 22:34:45 +02:00
parent 79c1a37931
commit 8365608bd5
32 changed files with 365 additions and 67 deletions
+1
View File
@@ -233,6 +233,7 @@ async function loadFiles(options = { insertHistory: true }) {
} else {
ui.renderFolders(folderList);
ui.renderFiles(fileList);
ui.resolveOwnerCells();
// check if a file was provided
if (app.viewFile) {
+12
View File
@@ -164,6 +164,9 @@ function setCurrentSection(section) {
sharedWithMeView.hide();
}
// Reset owner column — sections that need it re-enable it explicitly below.
ui.setOwnerColumnVisible(false);
// Hide photosView when switching to any other section
if (section !== 'photos' && photosView) {
photosView.hide();
@@ -211,6 +214,9 @@ function switchToSharedWithMeSection() {
// Show actions-bar with view toggle (no upload / new-folder in this view)
setActionsBarMode('sharedwithme');
// Show the Owner column — names are resolved async after render.
ui.setOwnerColumnVisible(true);
// Show the standard files container and respect grid/list preference
toggleFileContainer(true);
syncViewContainers();
@@ -227,6 +233,9 @@ function switchToFilesSection() {
// Set actions bar mode
setActionsBarMode('files', true);
// Show owner column in the Files section
ui.setOwnerColumnVisible(true);
// Show breadcrumb (only in Files view)
const breadcrumb = document.querySelector('.breadcrumb');
breadcrumb?.classList.remove('hidden');
@@ -258,6 +267,9 @@ function switchToFavoritesSection() {
// Set actions bar mode
setActionsBarMode('favorites');
// Show the Owner column — names are resolved async after render.
ui.setOwnerColumnVisible(true);
// Hide breadcrumb (only shown in Files view)
const breadcrumb = document.querySelector('.breadcrumb');
breadcrumb?.classList.add('hidden');
+52
View File
@@ -6,6 +6,7 @@
// @ts-check
import { shareModal } from '../components/shareModal.js';
import { createUserVignette } from '../components/userVignette.js';
import { escapeHtml, formatDateTime, formatFileSize } from '../core/formatters.js';
import { i18n } from '../core/i18n.js';
import { OxiIcons } from '../core/icons.js';
@@ -18,6 +19,7 @@ import { favorites } from '../features/library/favorites.js';
import { recent } from '../features/library/recent.js';
import { thumbnail } from '../features/thumbnail.js';
import { grants } from '../model/grants.js';
import { systemUsers } from '../model/systemUsers.js';
import { loadFiles } from './filesView.js';
import { updateHistory } from './main.js';
import { activateFilesUI, switchToFilesSection, syncViewContainers } from './navigation.js';
@@ -37,6 +39,12 @@ const ui = {
/** @type {HTMLDivElement | null} */
draggedItems: null,
/**
* Whether the Owner column is currently visible.
* Tracked so that newly rendered items can stamp the correct initial class.
*/
_ownerVisible: false,
/**
* Initialize context menus and dialogs
*/
@@ -436,6 +444,47 @@ const ui = {
syncViewContainers();
},
/**
* Show or hide the Owner column. When hidden, no name-resolution calls are made.
* Sections that show owner (SharedWithMe, Favorites) pass `true`; all others `false`.
* @param {boolean} visible
*/
setOwnerColumnVisible(visible) {
this._ownerVisible = visible;
document
.getElementById('files-list')
?.querySelectorAll('.owner-cell')
.forEach((cell) => {
cell.classList.toggle('hidden', !visible);
});
},
/**
* Asynchronously fill every un-resolved `.owner-cell` in the current list with
* the display name for its `data-owner-id` attribute.
*
* Call this after `renderFiles()` / `renderFolders()` in sections where the owner
* column is visible. Idempotent: cells already stamped with `data-owner-resolved`
* are skipped (safe to call on each "Load more" page append).
*
* When the column is hidden nothing calls this function, so `systemUsers` is never
* touched and no address-book requests are issued.
*
* @returns {Promise<void>}
*/
async resolveOwnerCells() {
const filesList = document.getElementById('files-list');
const cells = /** @type {NodeListOf<HTMLElement>} */ (filesList?.querySelectorAll('.owner-cell[data-owner-id]:not([data-owner-resolved])'));
if (!cells?.length) return;
systemUsers.prefetch(); // warm cache once (idempotent, fire-and-forget)
for (const cell of cells) {
const id = cell.dataset.ownerId;
cell.dataset.ownerResolved = '1';
if (!id) continue;
cell.replaceChildren(createUserVignette(id, 'sm'));
}
},
/**
* Update breadcrumb navigation from the breadcrumbPath array.
* Renders: Home > folder1 > folder2 > ...
@@ -1236,6 +1285,7 @@ const ui = {
<div class="file-badge file-badge-favorite ${isFav ? '' : 'hidden'}"><i class="fas fa-star favorite-star-inline"></i></div>
<div class="file-badge file-badge-shared ${isShared ? '' : 'hidden'}"><i class="fas fa-oxiexport"></i></div>
</div>
<div class="owner-cell${this._ownerVisible ? '' : ' hidden'}" data-owner-id="${escapeHtml(folder.owner_id || '')}"></div>
<div class="type-cell">${i18n.t('files.file_types.folder')}</div>
<div class="size-cell">--</div>
<div class="date-cell">${formattedDate}</div>
@@ -1290,6 +1340,7 @@ const ui = {
<div class="file-badge file-badge-favorite ${isFav ? '' : 'hidden'}"><i class="fas fa-star favorite-star-inline"></i></div>
<div class="file-badge file-badge-shared ${isShared ? '' : 'hidden'}"><i class="fas fa-oxiexport"></i></div>
</div>
<div class="owner-cell${this._ownerVisible ? '' : ' hidden'}" data-owner-id="${escapeHtml(file.owner_id || '')}"></div>
<div class="type-cell">${typeLabel}</div>
<div class="size-cell">${fileSize}</div>
<div class="date-cell">${formattedDate}</div>
@@ -1329,6 +1380,7 @@ const ui = {
<div class="list-header">
<div class="list-header-checkbox"><input type="checkbox" id="select-all-checkbox" title="Select all"></div>
<div data-i18n="files.name">Name</div>
<div class="owner-cell${this._ownerVisible ? '' : ' hidden'}" data-i18n="files.owner">Owner</div>
<div data-i18n="files.type">Type</div>
<div data-i18n="files.size">Size</div>
<div data-i18n="files.modified">Modified</div>