// @ts-check // TODO move to features/files/fileOperations.js ? /** * @typedef {Object} FolderInfo * @property {string} category * @property {number} created_at - timestamp * @property {string} icon_class * @property {string} icon_special_class * @property {string} id the uniq id of the folder * @property {boolean} is_root * @property {number} modified_at * @property {string} name * @property {string} owner_id * @property {string|null} parent_id the folder parent (null if is_root) * @property {string} path the full path */ /** * getFolder information * @param {string} id the id of the folder * @returns {Promise} */ async function getFolder(id) { /** @type {HeadersInit} */ const headers = { 'Cache-Control': 'no-cache, no-store, must-revalidate', Pragma: 'no-cache' }; /** @type {RequestInit} */ const requestOptions = { headers, credentials: 'same-origin', cache: 'no-store' }; let folderInformations = await fetch(`/api/folders/${id}`, requestOptions); if (folderInformations.ok) { return folderInformations.json(); } else { console.warn(`Error fetching folder ${id}`); return Promise.reject(null); } } /** * rebuild breadcrumb from selected folder (iterate up to root) */ async function rebuildBreadCrumb() { const app = window.app; /** * Store the leaf (this is the current displayed folder) * @type {FolderInfo | null} */ let currentFolderInfo = null; // rebuild full breadcrumb, // TODO: to optimize, data may already be known / or ETAG could be interesting to reduce load app.breadcrumbPath = []; /** @type {string | null} */ let id = app.currentPath; // recurse from selected folder to root while (id !== null) { console.log(`fetching folder information for folder ${id}`); try { let folderInfo = await getFolder(id); // store the Leaf which is the current folder if (currentFolderInfo === null) { currentFolderInfo = folderInfo; } // XXX do not enter root into bread crumb updateBreadcrumb() method always display it if (!folderInfo.is_root) { app.breadcrumbPath.unshift({ id: folderInfo.id, name: folderInfo.name }); } // iterate to parent folder id = folderInfo.parent_id; } catch (e) { console.log(`Error loading information from folder ${app.currentPath}, falling back to ${app.userHomeFolderId}`); // fallback of root window.uiNotifications.show( 'error: folder not found or permission denied', 'the given folder is not available or you do not have sufficient rights' ); app.breadcrumbPath = []; id = app.userHomeFolderId; app.currentPath = id; } } // store informations on the current folder app.currentFolderInfo = currentFolderInfo; } /** * Files view loading logic * * @param {Object} options * @param {boolean} [options.insertHistory] add browser history (default true) * @param {boolean} [options.forceRefresh] force refresh of content */ async function loadFiles(options = { insertHistory: true }) { const app = window.app; const elements = window.appElements; try { console.log('Starting loadFiles() - loading files...', options); const forceRefresh = options.forceRefresh || false; if (window.isLoadingFiles) { console.log('A file load is already in progress, ignoring request'); return; } window.isLoadingFiles = true; // This to avoid blinking page, a better solution would be to put loading on an overlay and remove timeout let loadingFiles = setTimeout(() => { // display loader after few delay (will be canceled if result take less time) window.ui.showError(`
${window.i18n ? window.i18n.t('files.loading') : 'Loading files…'}
`); }, 100); if (!app.userHomeFolderId) { await window.resolveHomeFolder(); } const timestamp = new Date().getTime(); await rebuildBreadCrumb(); // request a breadcrumb paint window.ui.updateBreadcrumb(); window.updateHistory(options.insertHistory || false); let url; if (!app.currentPath || app.currentPath === '') { if (app.userHomeFolderId) { url = `/api/folders/${app.userHomeFolderId}/listing?t=${timestamp}`; app.currentPath = app.userHomeFolderId; app.breadcrumbPath = []; window.ui.updateBreadcrumb(); console.log(`Loading user folder: ${app.userHomeFolderName} (${app.userHomeFolderId})`); } else { url = `/api/folders?t=${timestamp}`; console.warn('Emergency fallback to root folder - this should not normally happen'); } } else { url = `/api/folders/${app.currentPath}/listing?t=${timestamp}`; console.log(`Loading subfolder content: ${app.currentPath}`); } /** @type {HeadersInit} */ const headers = { 'Cache-Control': 'no-cache, no-store, must-revalidate', Pragma: 'no-cache' }; /** @type {RequestInit} */ const requestOptions = { headers, credentials: 'same-origin', cache: 'no-store' }; if (forceRefresh) { url += `&force_refresh=true`; // @ts-ignore requestOptions.headers['X-Force-Refresh'] = 'true'; console.log('Forcing complete refresh ignoring cache'); } console.log(`Loading listing from ${url}`); const response = await fetch(url, requestOptions); // not required anymore clearTimeout(loadingFiles); if (response.status === 401 || response.status === 403) { console.warn('Auth error when loading files, showing empty list'); // FIXME: i18n window.ui.showError(`

Could not load files

`); return; } if (!response.ok) { throw new Error(`Server responded with status: ${response.status}`); } const listing = await response.json(); window.ui._items.clear(); window.ui.resetFilesList(); if (window.multiSelect) { window.multiSelect.clear(); window.multiSelect.init(); // this will wire buttons & select-all-checkbox } const folderList = Array.isArray(listing.folders) ? listing.folders : []; const fileList = Array.isArray(listing.files) ? listing.files : []; if (folderList.length === 0 && fileList.length === 0) { window.ui.showEmptyList(); } else { window.ui.renderFolders(folderList); window.ui.renderFiles(fileList); // check if a file was provided if (window.app.viewFile) { let fileFound = null; // lookup for the given fle for( const file of fileList) { if (file.id === window.app.viewFile) { fileFound = file; break; } } if (fileFound) { console.log(`file ${window.app.viewFile} found, calling viewer`); await window.inlineViewer.openFile(fileFound); } else { // remove file console.log(`file ${window.app.viewFile} not found`); window.app.viewFile = null; // correct url/history as file is not found window.updateHistory( false); } } } console.log(`Loaded ${folderList.length} folders and ${fileList.length} files`); } catch (error) { console.error('Error loading folders:', error); window.ui.showNotification('Error', 'Could not load files and folders'); } finally { window.isLoadingFiles = false; } } window.loadFiles = loadFiles;