fix(thumbnails): display thumbnails in file grid/list and fix WOPI intercepting all file opens

wopiEditor.canEdit() is async but was called without await, so the
returned Promise was always truthy — routing every file click to the
WOPI editor (which 404'd). Made openFile() async and added await in
both ui.js and inlineViewer.js.

Added <img> thumbnail elements to _createFileCard() and
_createFileItem() for image files, loading from
/api/files/{id}/thumbnail/icon with lazy loading and error fallback.
This commit is contained in:
Jared Wolff
2026-03-04 21:44:02 -05:00
parent 6db4e07538
commit 2a75e83752
4 changed files with 20 additions and 4 deletions
+11
View File
@@ -322,6 +322,17 @@ button.favorite-star {
overflow: hidden; overflow: hidden;
} }
/* Thumbnail image inside file-icon (grid and list views) */
.file-icon .file-thumb {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 1;
}
.file-icon.image-icon::before { .file-icon.image-icon::before {
content: ""; content: "";
position: absolute; position: absolute;
+3
View File
@@ -145,6 +145,9 @@
.file-item .file-icon.image-icon { .file-item .file-icon.image-icon {
background-color: #e0f2fe; background-color: #e0f2fe;
position: relative;
overflow: hidden;
border-radius: 4px;
} }
.file-item .file-icon.image-icon i, .file-item .file-icon.image-icon i,
+4 -2
View File
@@ -747,13 +747,13 @@ const ui = {
return null; return null;
}; };
const openFile = (file) => { const openFile = async (file) => {
if (!file) return; if (!file) return;
if (window.recent) { if (window.recent) {
document.dispatchEvent(new CustomEvent('file-accessed', { detail: { file } })); document.dispatchEvent(new CustomEvent('file-accessed', { detail: { file } }));
} }
// WOPI editor intercept: open Office documents in the WOPI editor // WOPI editor intercept: open Office documents in the WOPI editor
if (window.wopiEditor && window.wopiEditor.canEdit(file.name)) { if (window.wopiEditor && await window.wopiEditor.canEdit(file.name)) {
window.wopiEditor.openInModal(file.id, file.name, 'edit'); window.wopiEditor.openInModal(file.id, file.name, 'edit');
return; return;
} }
@@ -1139,6 +1139,7 @@ const ui = {
<i class="${isFileFav ? 'fas' : 'far'} fa-star"></i> <i class="${isFileFav ? 'fas' : 'far'} fa-star"></i>
</button> </button>
<div class="file-icon ${iconSpecialClass}"> <div class="file-icon ${iconSpecialClass}">
${iconSpecialClass === 'image-icon' ? `<img class="file-thumb" src="/api/files/${file.id}/thumbnail/icon" loading="lazy" alt="" onerror="this.style.display='none'">` : ''}
<i class="${iconClass}"></i> <i class="${iconClass}"></i>
</div> </div>
<div class="file-name">${escapeHtml(file.name)}</div> <div class="file-name">${escapeHtml(file.name)}</div>
@@ -1176,6 +1177,7 @@ const ui = {
<div class="list-item-checkbox"><input type="checkbox" class="item-checkbox"></div> <div class="list-item-checkbox"><input type="checkbox" class="item-checkbox"></div>
<div class="name-cell"> <div class="name-cell">
<div class="file-icon ${iconSpecialClass}"> <div class="file-icon ${iconSpecialClass}">
${iconSpecialClass === 'image-icon' ? `<img class="file-thumb" src="/api/files/${file.id}/thumbnail/icon" loading="lazy" alt="" onerror="this.style.display='none'">` : ''}
<i class="${iconClass}"></i> <i class="${iconClass}"></i>
</div> </div>
<span>${escapeHtml(file.name)}</span> <span>${escapeHtml(file.name)}</span>
+2 -2
View File
@@ -88,11 +88,11 @@ class InlineViewer {
console.log('Inline viewer initialized'); console.log('Inline viewer initialized');
} }
openFile(file) { async openFile(file) {
console.log('Opening file:', file); console.log('Opening file:', file);
// WOPI editor intercept: open Office documents in the WOPI editor // WOPI editor intercept: open Office documents in the WOPI editor
if (window.wopiEditor && window.wopiEditor.canEdit(file.name)) { if (window.wopiEditor && await window.wopiEditor.canEdit(file.name)) {
window.wopiEditor.openInModal(file.id, file.name, 'edit'); window.wopiEditor.openInModal(file.id, file.name, 'edit');
return; return;
} }