From 615657a7bee664947ac3cd976c3ebddfcc99de31 Mon Sep 17 00:00:00 2001 From: Edouard Vanbelle Date: Fri, 29 May 2026 14:06:28 +0200 Subject: [PATCH] fix(shares): fix outgoing shares when browsing in files, fix shareBadge action to open shareDialog --- static/js/app/filesView.js | 17 ++++++++++++++++- static/js/app/main.js | 9 ++++++--- static/js/app/navigation.js | 9 ++++++++- static/js/components/resourceList.js | 27 +++++++++++++++++++++++---- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/static/js/app/filesView.js b/static/js/app/filesView.js index 086e4964..e8dfd10f 100644 --- a/static/js/app/filesView.js +++ b/static/js/app/filesView.js @@ -15,6 +15,7 @@ */ import { ResourceListComponent } from '../components/resourceList.js'; +import { shareModal } from '../components/shareModal.js'; import { normalizeDateBucket, sizeBucket } from '../core/formatters.js'; import { i18n } from '../core/i18n.js'; import * as viewPrefs from '../core/viewPrefs.js'; @@ -218,6 +219,12 @@ function _ensureComponent() { _component?.setFavoriteVisualState(item.id, type, true); } }, + onShareBadgeClick: (item) => { + const isFile = 'mime_type' in item; + shareModal.open(item, isFile ? 'file' : 'folder', () => { + grants.fetchOutgoingGrants().then(() => refreshSharedBadges()); + }); + }, onContextMenu: (item, e) => ui.showContextMenuForItem(item, e), onSelectionChange: (selectedItems) => { batchToolbar._selected.clear(); @@ -454,4 +461,12 @@ async function loadFiles(options = { insertHistory: true }) { } } -export { addItem, filesView, loadFiles }; +/** + * Re-evaluate the shared badge for every item currently rendered in the Files list. + * Call this after the outgoing grants cache has been refreshed. + */ +function refreshSharedBadges() { + _component?.refreshSharedBadges(); +} + +export { addItem, filesView, loadFiles, refreshSharedBadges }; diff --git a/static/js/app/main.js b/static/js/app/main.js index ec71788d..26f67204 100644 --- a/static/js/app/main.js +++ b/static/js/app/main.js @@ -529,6 +529,12 @@ function initApp() { window.addEventListener('authenticationDone', async () => { // Check if a context was provided in the URL const hashContext = deserializeHash(); + + // Always fetch grants so shared badges are correct regardless of the + // initial section. Fire in the background — don't block section init. + grants.fetchIncomingGrants(); + grants.fetchOutgoingGrants(); + switchSectionTo(hashContext.section); if (hashContext.section === 'files') { if (hashContext.path) { @@ -540,9 +546,6 @@ function initApp() { app.viewFile = hashContext.file; } - // get grants (xxx: async methods) - await grants.fetchIncomingGrants(); - await grants.fetchOutgoingGrants(); loadFiles(); } }); diff --git a/static/js/app/navigation.js b/static/js/app/navigation.js index c6d89bba..adec3924 100644 --- a/static/js/app/navigation.js +++ b/static/js/app/navigation.js @@ -10,11 +10,12 @@ import { batchToolbar } from '../features/files/batchToolbar.js'; import { favorites } from '../features/library/favorites.js'; import { musicView } from '../features/library/music.js'; import { photosView } from '../features/library/photos.js'; +import { grants } from '../model/grants.js'; import { favoritesView } from '../views/favorites/favoritesView.js'; import { mySharesView } from '../views/myShares/mySharesView.js'; import { recentView } from '../views/recent/recentView.js'; import { sharedWithMeView } from '../views/sharedWithMe/sharedWithMeView.js'; -import { filesView, loadFiles } from './filesView.js'; +import { filesView, loadFiles, refreshSharedBadges } from './filesView.js'; import { setActionsBarMode, setGroupByView, syncGroupByMenu } from './main.js'; import { app, appElements } from './state.js'; import { loadTrashItems } from './trashView.js'; @@ -300,6 +301,12 @@ function switchToFilesSection() { if (batchToolbar) batchToolbar.clear(); loadFiles(); + + // Refresh outgoing grants in the background and repaint badges once done. + // Badges are rendered synchronously from the in-memory cache, so any staleness + // from navigating away and back (or starting on a different section) is corrected + // without blocking the file list render. + grants.fetchOutgoingGrants().then(() => refreshSharedBadges()); } function switchToFavoritesSection() { diff --git a/static/js/components/resourceList.js b/static/js/components/resourceList.js index 4f96b9cf..564c7807 100644 --- a/static/js/components/resourceList.js +++ b/static/js/components/resourceList.js @@ -55,7 +55,9 @@ import { createUserVignette } from './userVignette.js'; * @property {(item: FileItem|FolderItem) => Promise} [onFavoriteToggle] * Called when the user clicks the favorite-star button. * @property {(item: FileItem|FolderItem, event: MouseEvent) => void} [onContextMenu] - * Called for the three-dots button click, right-click, and shared-badge click. + * Called for the three-dots button click and right-click. + * @property {(item: FileItem|FolderItem) => void} [onShareBadgeClick] + * Called when the user clicks the shared badge. Falls back to onContextMenu if absent. * @property {(selected: Array) => void} [onSelectionChange] * Called whenever the selection set changes. */ @@ -333,6 +335,19 @@ export class ResourceListComponent { item.querySelector('.file-badge-shared')?.classList.toggle('hidden', !isShared); } + /** + * Re-evaluate the shared badge for every currently rendered item using the + * `isShared` callback from config. Call this after the grants cache is refreshed. + */ + refreshSharedBadges() { + if (!this._cfg.isShared) return; + for (const item of this._items.values()) { + const isFile = 'mime_type' in item; + const type = /** @type {'file'|'folder'} */ (isFile ? 'file' : 'folder'); + this.setSharedVisualState(item.id, type, this._cfg.isShared(item.id, type)); + } + } + // ── Private helpers ───────────────────────────────────────────────────── /** @@ -532,14 +547,18 @@ export class ResourceListComponent { }); } - // Shared-badge click → treat as context-menu trigger (e.g. open share modal) - if (cfg.showShareBadge && cfg.onContextMenu) { + // Shared-badge click → open share modal (or fall back to context menu) + if (cfg.showShareBadge && (cfg.onShareBadgeClick || cfg.onContextMenu)) { const badge = el.querySelector('.file-badge-shared'); badge?.addEventListener('click', (e) => { e.stopPropagation(); e.stopImmediatePropagation(); e.preventDefault(); - cfg.onContextMenu?.(item, /** @type {MouseEvent} */ (e)); + if (cfg.onShareBadgeClick) { + cfg.onShareBadgeClick(item); + } else { + cfg.onContextMenu?.(item, /** @type {MouseEvent} */ (e)); + } }); } }