diff --git a/biome.json b/biome.json index 7f0377a0..20ba54c3 100644 --- a/biome.json +++ b/biome.json @@ -14,7 +14,8 @@ "rules": { "recommended": true, "correctness": { - "noUnusedVariables": "warn" + "noUnusedVariables": "warn", + "noUndeclaredVariables": "error" }, "style": { "noDescendingSpecificity": "off" diff --git a/build.rs b/build.rs index 6dfbb442..d653cc82 100644 --- a/build.rs +++ b/build.rs @@ -113,15 +113,19 @@ fn process_release(manifest_dir: &Path, static_dir: &Path, out_dir: &Path) { // ── 4. Minify ALL individual CSS in static-dist/ ───────────────────────── minify_tree_css(&dist_dir.join("css")); - // ── 5. Build JS bundle for index.html ──────────────────────────────────── + // ── 5. Bundle all ES modules into one IIFE ─────────────────────────────── + // Walk the import graph starting from every ")); - defer_done = true; + // ── Replace all type="module" scripts with single bundle ───────────── + if t.starts_with("" + )); + js_done = true; } continue; } - // ── Drop "Service Worker Registration" comment ────────────────────── - if t.contains("Service Worker Registration") { - continue; - } - - // ── Drop "Styles" / "Scripts" section comments ────────────────────── - if t.starts_with(" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/js/app/authSession.js b/static/js/app/authSession.js index f756c8b9..a1ef161e 100644 --- a/static/js/app/authSession.js +++ b/static/js/app/authSession.js @@ -2,6 +2,12 @@ * Authentication/session bootstrap and home-folder resolution */ +import { getCsrfHeaders } from '../core/csrf.js'; +import { loadFiles } from './filesView.js'; +import { updateStorageUsageDisplay } from './main.js'; +import { app } from './state.js'; +import { ui } from './ui.js'; + async function refreshUserData() { const USER_DATA_KEY = 'oxicloud_user'; @@ -24,7 +30,7 @@ async function refreshUserData() { console.log('Storage from server: used=', userData.storage_used_bytes, 'quota=', userData.storage_quota_bytes); localStorage.setItem(USER_DATA_KEY, JSON.stringify(userData)); - window.updateStorageUsageDisplay(userData); + updateStorageUsageDisplay(userData); return userData; } catch (error) { console.error('Error refreshing user data:', error); @@ -89,7 +95,7 @@ async function checkAuthentication() { if (menuName) menuName.textContent = userData.username; if (menuEmail) menuEmail.textContent = userData.email || ''; - window.updateStorageUsageDisplay(userData); + updateStorageUsageDisplay(userData); // Validate session BEFORE loading files to avoid 401 race condition const freshData = await refreshUserData(); @@ -133,8 +139,8 @@ async function checkAuthentication() { document.querySelectorAll('.user-avatar, .user-menu-avatar').forEach((el) => { el.textContent = userInitials; }); - window.updateStorageUsageDisplay(freshData); - resolveHomeFolder().then(() => window.loadFiles()); + updateStorageUsageDisplay(freshData); + resolveHomeFolder().then(() => loadFiles()); } else { console.warn('Could not retrieve user data, redirecting to login'); localStorage.removeItem(USER_DATA_KEY); @@ -154,8 +160,6 @@ async function checkAuthentication() { } async function resolveHomeFolder() { - const app = window.app; - if (app.userHomeFolderId) return; try { const response = await fetch('/api/folders', { @@ -173,22 +177,20 @@ async function resolveHomeFolder() { app.userHomeFolderName = home.name; app.currentPath = home.id; app.breadcrumbPath = []; - window.ui.updateBreadcrumb(); + ui.updateBreadcrumb(); console.log(`Home folder resolved: ${home.name} (${home.id})`); } else { console.warn('No root folders found for user'); app.currentPath = ''; app.breadcrumbPath = []; - window.ui.updateBreadcrumb(); + ui.updateBreadcrumb(); } } catch (error) { console.error('Error resolving home folder:', error); app.currentPath = ''; app.breadcrumbPath = []; - window.ui.updateBreadcrumb(); + ui.updateBreadcrumb(); } } -window.refreshUserData = refreshUserData; -window.checkAuthentication = checkAuthentication; -window.resolveHomeFolder = resolveHomeFolder; +export { checkAuthentication, refreshUserData, resolveHomeFolder }; diff --git a/static/js/app/bootstrap.js b/static/js/app/bootstrap.js index 54d45a16..fcdd327f 100644 --- a/static/js/app/bootstrap.js +++ b/static/js/app/bootstrap.js @@ -2,13 +2,10 @@ * OxiCloud - App bootstrap * Isolated startup trigger for the main application initializer. */ +import { initApp } from './main.js'; if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => { - if (typeof window.initApp === 'function') { - window.initApp(); - } - }); -} else if (typeof window.initApp === 'function') { - window.initApp(); + document.addEventListener('DOMContentLoaded', initApp); +} else { + initApp(); } diff --git a/static/js/app/filesView.js b/static/js/app/filesView.js index 4faa2221..81fff22e 100644 --- a/static/js/app/filesView.js +++ b/static/js/app/filesView.js @@ -1,5 +1,16 @@ // @ts-check +import { i18n } from '../core/i18n.js'; +import { inlineViewer } from '../features/files/inlineViewer.js'; +import { multiSelect } from '../features/files/multiSelect.js'; +import { resolveHomeFolder } from './authSession.js'; +import { updateHistory } from './main.js'; +import { app } from './state.js'; +import { ui } from './ui.js'; +import { uiNotifications } from './uiNotifications.js'; + +let isLoadingFiles = false; + // TODO move to features/files/fileOperations.js ? /** * @typedef {Object} FolderInfo @@ -48,8 +59,6 @@ async function getFolder(id) { * 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} @@ -87,10 +96,7 @@ async function rebuildBreadCrumb() { } 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' - ); + 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; @@ -109,33 +115,31 @@ async function rebuildBreadCrumb() { * @param {boolean} [options.forceRefresh] force refresh of content */ async function loadFiles(options = { insertHistory: true }) { - const app = window.app; - try { console.log('Starting loadFiles() - loading files...', options); const forceRefresh = options.forceRefresh || false; - if (window.isLoadingFiles) { + if (isLoadingFiles) { console.log('A file load is already in progress, ignoring request'); return; } - window.isLoadingFiles = true; + isLoadingFiles = true; // This to avoid blinking page, a better solution would be to put loading on an overlay and remove timeout const loadingFiles = setTimeout(() => { // display loader after few delay (will be canceled if result take less time) - window.ui.showError(` + ui.showError(`
Could not load files
`); + ui.showError(`Could not load files
`); return; } @@ -203,44 +207,44 @@ async function loadFiles(options = { insertHistory: true }) { 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 + ui._items.clear(); + ui.resetFilesList(); + if (multiSelect) { + multiSelect.clear(); + 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(); + ui.showEmptyList(); } else { - window.ui.renderFolders(folderList); - window.ui.renderFiles(fileList); + ui.renderFolders(folderList); + ui.renderFiles(fileList); // check if a file was provided - if (window.app.viewFile) { + if (app.viewFile) { let fileFound = null; // lookup for the given fle for (const file of fileList) { - if (file.id === window.app.viewFile) { + if (file.id === app.viewFile) { fileFound = file; break; } } if (fileFound) { - console.log(`file ${window.app.viewFile} found, calling viewer`); - await window.inlineViewer.openFile(fileFound); + console.log(`file ${app.viewFile} found, calling viewer`); + await inlineViewer.openFile(fileFound); } else { // remove file - console.log(`file ${window.app.viewFile} not found`); - window.app.viewFile = null; + console.log(`file ${app.viewFile} not found`); + app.viewFile = null; // correct url/history as file is not found - window.updateHistory(false); + updateHistory(false); } } } @@ -248,10 +252,10 @@ async function loadFiles(options = { insertHistory: true }) { 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'); + ui.showNotification('Error', 'Could not load files and folders'); } finally { - window.isLoadingFiles = false; + isLoadingFiles = false; } } -window.loadFiles = loadFiles; +export { loadFiles }; diff --git a/static/js/app/main.js b/static/js/app/main.js index d7ac93cb..fdba54f0 100644 --- a/static/js/app/main.js +++ b/static/js/app/main.js @@ -3,8 +3,31 @@ * This file contains the core functionality, initialization and state management */ -const app = window.app; -const elements = window.appElements; +import { formatFileSize, formatQuotaSize } from '../core/formatters.js'; +import { i18n } from '../core/i18n.js'; +import { Modal } from '../core/modal.js'; +import { fileOps } from '../features/files/fileOperations.js'; +import { multiSelect } from '../features/files/multiSelect.js'; +import { favorites } from '../features/library/favorites.js'; +import { recent } from '../features/library/recent.js'; +import { fileSharing } from '../features/sharing/fileSharing.js'; +import { sharedView } from '../views/shared/sharedView.js'; +import { checkAuthentication } from './authSession.js'; +import { loadFiles } from './filesView.js'; +import { + switchToFavoritesSection, + switchToFilesSection, + switchToMusicSection, + switchToPhotosSection, + switchToRecentFilesSection, + switchToSharedSection, + switchToTrashSection +} from './navigation.js'; +import { performSearch } from './searchView.js'; +import { app, appElements as elements } from './state.js'; +import { loadTrashItems } from './trashView.js'; +import { ui } from './ui.js'; +import { setupUserMenu } from './userMenu.js'; // Upload dropdown listener state (prevents accumulated listeners) /** @type { function | null } */ @@ -125,8 +148,8 @@ function setActionsBarMode(mode, force = false) { elements.gridViewBtn = document.getElementById('grid-view-btn'); elements.listViewBtn = document.getElementById('list-view-btn'); - if (window.i18n?.translateElement) { - window.i18n.translateElement(elements.actionsBar); + if (i18n?.translateElement) { + i18n.translateElement(elements.actionsBar); } if (mode === 'files') { @@ -159,7 +182,7 @@ function setupActionsBarDelegation() { break; } case 'new-folder-btn': { - const folderName = await window.Modal.promptNewFolder(); + const folderName = await Modal.promptNewFolder(); if (folderName) { fileOps.createFolder(folderName); } @@ -173,14 +196,14 @@ function setupActionsBarDelegation() { break; case 'empty-trash-btn': if (await fileOps.emptyTrash()) { - window.loadTrashItems(); + loadTrashItems(); } break; case 'clear-recent-btn': - if (window.recent) { - window.recent.clearRecentFiles(); - window.recent.displayRecentFiles(); - window.ui.showNotification('Cleanup completed', 'Recent files history has been cleared'); + if (recent) { + recent.clearRecentFiles(); + recent.displayRecentFiles(); + ui.showNotification('Cleanup completed', 'Recent files history has been cleared'); } break; default: @@ -245,8 +268,6 @@ function deserializeHash() { * @param {boolean} insertHistory true to change url and browser's history, false to change url only */ function updateHistory(insertHistory) { - const app = window.app; - const historyData = { section: app.currentSection, id: app.currentFolder, @@ -259,8 +280,8 @@ function updateHistory(insertHistory) { historyData.id = app.currentFolder; historyUrl = historyUrl.concat('/folder/', app.currentFolderInfo.id); - if (window.app.viewFile) { - historyUrl = historyUrl.concat('/file/', window.app.viewFile); + if (app.viewFile) { + historyUrl = historyUrl.concat('/file/', app.viewFile); } // update title document.title = `OxiCloud: ${app.currentFolderInfo.path}`; @@ -281,7 +302,7 @@ function updateHistory(insertHistory) { * @returns */ function switchSectionTo(section) { - if (window.app.currentSection === section) + if (app.currentSection === section) // no change ... return; @@ -324,8 +345,8 @@ function initApp() { cacheElements(); // Initialize file sharing module first - if (window.fileSharing?.init) { - window.fileSharing.init(); + if (fileSharing?.init) { + fileSharing.init(); } else { console.warn('fileSharing module not fully initialized'); } @@ -338,35 +359,26 @@ function initApp() { // Setup event listeners setupEventListeners(); - // Ensure inline viewer is initialized - if (!window.inlineViewer && typeof InlineViewer !== 'undefined') { - try { - window.inlineViewer = new InlineViewer(); - } catch (e) { - console.error('Error initializing inline viewer:', e); - } - } - // Initialize favorites module if available - if (window.favorites?.init) { + if (favorites?.init) { console.log('Initializing favorites module'); - window.favorites.init(); + favorites.init(); } else { console.warn('Favorites module not available or not initializable'); } // Initialize recent files module if available - if (window.recent?.init) { + if (recent?.init) { console.log('Initializing recent files module'); - window.recent.init(); + recent.init(); } else { console.warn('Recent files module not available or not initializable'); } // Initialize multi-select / batch actions - if (window.multiSelect?.init) { + if (multiSelect?.init) { console.log('Initializing multi-select module'); - window.multiSelect.init(); + multiSelect.init(); } window.addEventListener('authenticationDone', () => { @@ -376,33 +388,33 @@ function initApp() { if (hashContext.section === 'files') { if (hashContext.path) { console.log(`init: reusing folder from hash URL: ${hashContext.path}`); - window.app.currentPath = hashContext.path; + app.currentPath = hashContext.path; } if (hashContext.file !== null) { - window.app.viewFile = hashContext.file; + app.viewFile = hashContext.file; } - window.loadFiles(); + loadFiles(); } }); // Wait for translations to load before checking authentication - if (window.i18n?.isLoaded?.()) { + if (i18n?.isLoaded?.()) { // Translations already loaded, proceed with authentication - window.checkAuthentication(); + checkAuthentication(); } else { // Wait for translations to be loaded before proceeding console.log('Waiting for translations to load...'); window.addEventListener('translationsLoaded', () => { console.log('Translations loaded, proceeding with authentication'); - window.checkAuthentication(); + checkAuthentication(); }); // Set a timeout as a fallback in case translations take too long setTimeout(() => { - if (!window.i18n?.isLoaded?.()) { + if (!i18n?.isLoaded?.()) { console.warn('Translations loading timeout, proceeding with authentication anyway'); - window.checkAuthentication(); + checkAuthentication(); } }, 3000); // 3 second timeout } @@ -493,14 +505,14 @@ function setupEventListeners() { const hashContext = deserializeHash(); switchSectionTo(hashContext.section); if (hashContext.path) { - window.app.currentPath = hashContext.path; - window.loadFiles({ insertHistory: false }); + app.currentPath = hashContext.path; + loadFiles({ insertHistory: false }); } } else { // change is from history, data provided in event switchSectionTo(e.state.section); - window.app.currentPath = e.state.id; - window.loadFiles({ insertHistory: false }); + app.currentPath = e.state.id; + loadFiles({ insertHistory: false }); } }); @@ -512,19 +524,19 @@ function setupEventListeners() { const query = elements.searchInput.value.trim(); // In shared view, filter locally - if (app.isSharedView && window.sharedView) { - window.sharedView.filterAndSortItems(); + if (app.isSharedView && sharedView) { + sharedView.filterAndSortItems(); return; } if (query) { - window.performSearch(query); + performSearch(query); } else if (app.isSearchMode) { // If search is empty and we're in search mode, return to normal view app.isSearchMode = false; app.currentPath = ''; ui.updateBreadcrumb(''); - window.loadFiles(); + loadFiles(); } } }); @@ -536,7 +548,7 @@ function setupEventListeners() { if (query.length >= SEARCH_MIN_CHARS) { searchDebounceTimer = setTimeout(() => { - window.performSearch(query); + performSearch(query); }, SEARCH_DEBOUNCE_MS); } else if (query.length === 0 && app.isSearchMode) { // User cleared the search input — return to normal view @@ -544,7 +556,7 @@ function setupEventListeners() { app.isSearchMode = false; app.currentPath = ''; ui.updateBreadcrumb(''); - window.loadFiles(); + loadFiles(); }, SEARCH_DEBOUNCE_MS); } }); @@ -554,7 +566,7 @@ function setupEventListeners() { if (searchDebounceTimer) clearTimeout(searchDebounceTimer); const query = elements.searchInput.value.trim(); if (query) { - window.performSearch(query); + performSearch(query); } }); @@ -627,12 +639,12 @@ function setupEventListeners() { default: // Use the proper switchToFilesView function which handles all UI restoration - window.switchToFilesSection(); + switchToFilesSection(); // FIXME: because fileview handles it: need to converge code _updateHistory = false; } - document.title = `OxiCloud: ${window.i18n.t(itemI18nKey)}`; + document.title = `OxiCloud: ${i18n.t(itemI18nKey)}`; if (_updateHistory) { updateHistory(true); @@ -649,7 +661,7 @@ function setupEventListeners() { } // User menu - window.setupUserMenu(); + setupUserMenu(); // Global events to close context menus and deselect cards document.addEventListener('click', (e) => { @@ -666,18 +678,19 @@ function setupEventListeners() { }); } -// Expose needed functions to global scope -window.setActionsBarMode = setActionsBarMode; +// View-switching actions moved to app/navigation.js -// Set up global selectFolder function for navigation -window.selectFolder = (id, name) => { +/** + * Navigate into a folder and refresh the file list. + * @param {string} id + * @param {string} name + */ +export function selectFolder(id, name) { app.breadcrumbPath.push({ id, name }); app.currentPath = id; ui.updateBreadcrumb(); - window.loadFiles(); -}; - -// View-switching actions moved to app/navigation.js + loadFiles(); +} /** * Update the storage usage display with the user's actual storage usage @@ -719,8 +732,8 @@ function updateStorageUsageDisplay(userData) { storageInfo.removeAttribute('data-i18n'); // Use i18n if available - if (window.i18n?.t) { - storageInfo.textContent = window.i18n.t('storage.used', { + if (i18n?.t) { + storageInfo.textContent = i18n.t('storage.used', { percentage: usagePercentage, used: usedFormatted, total: quotaFormatted @@ -733,9 +746,4 @@ function updateStorageUsageDisplay(userData) { console.log(`Updated storage display: ${usagePercentage}% (${usedFormatted} / ${quotaFormatted})`); } -window.updateStorageUsageDisplay = updateStorageUsageDisplay; - -// Initialize app when DOM is ready -window.initApp = initApp; -window.updateHistory = updateHistory; -window.deserializeHash = deserializeHash; +export { deserializeHash, initApp, setActionsBarMode, updateHistory, updateStorageUsageDisplay }; diff --git a/static/js/app/navigation.js b/static/js/app/navigation.js index 14269646..6884eff3 100644 --- a/static/js/app/navigation.js +++ b/static/js/app/navigation.js @@ -3,6 +3,19 @@ * Extracted from main.js to keep navigation concerns isolated. */ +import { i18n } from '../core/i18n.js'; +import { multiSelect } from '../features/files/multiSelect.js'; +import { favorites } from '../features/library/favorites.js'; +import { musicView } from '../features/library/music.js'; +import { photosView } from '../features/library/photos.js'; +import { recent } from '../features/library/recent.js'; +import { sharedView } from '../views/shared/sharedView.js'; +import { loadFiles } from './filesView.js'; +import { setActionsBarMode } from './main.js'; +import { app, appElements } from './state.js'; +import { loadTrashItems } from './trashView.js'; +import { ui } from './ui.js'; + /** * Sync the hidden class and inline display for the grid/list containers * based on the current view preference. @@ -12,7 +25,7 @@ function syncViewContainers() { const gridViewBtn = document.getElementById('grid-view-btn'); const listViewBtn = document.getElementById('list-view-btn'); - const isGrid = window.app.currentView === 'grid'; + const isGrid = app.currentView === 'grid'; if (isGrid) { filesList.classList.remove('files-list-view'); filesList.classList.add('files-grid-view'); @@ -89,13 +102,6 @@ function initSidebarToggle() { } }); }); - - // Expose functions globally - window.sidebarToggle = { - open: openSidebar, - close: closeSidebar, - toggle: toggleSidebar - }; } // Initialize sidebar toggle when DOM is ready @@ -128,17 +134,17 @@ function getSectionFromNavItem(navItem) { * @returns {boolean} true if the section changed */ function setCurrentSection(section) { - if (window.app.currentSection === section) return false; + if (app.currentSection === section) return false; // Set all view flags - true for active section, false for others Object.entries(VIEW_FLAGS).forEach(([key, flag]) => { - window.app[flag] = key === section; + app[flag] = key === section; }); - window.app.currentSection = section; + app.currentSection = section; // Update nav item active classes by finding matching item from DOM - window.appElements.navItems.forEach((item) => { + appElements.navItems.forEach((item) => { const itemSection = getSectionFromNavItem(item); item.classList.toggle('active', itemSection === section); }); @@ -146,22 +152,22 @@ function setCurrentSection(section) { // Update page title const titleKey = `nav.${section}`; const defaultTitle = section.charAt(0).toUpperCase() + section.slice(1); - window.appElements.pageTitle.textContent = window.i18n ? window.i18n.t(titleKey) : defaultTitle; - window.appElements.pageTitle.setAttribute('data-i18n', titleKey); + appElements.pageTitle.textContent = i18n ? i18n.t(titleKey) : defaultTitle; + appElements.pageTitle.setAttribute('data-i18n', titleKey); // Hide sharedView when switching to any other section - if (section !== 'shared' && window.sharedView) { - window.sharedView.hide(); + if (section !== 'shared' && sharedView) { + sharedView.hide(); } // Hide photosView when switching to any other section - if (section !== 'photos' && window.photosView) { - window.photosView.hide(); + if (section !== 'photos' && photosView) { + photosView.hide(); } // Hide musicView when switching to any other section - if (section !== 'music' && window.musicView) { - window.musicView.hide(); + if (section !== 'music' && musicView) { + musicView.hide(); } return true; @@ -175,27 +181,27 @@ function switchToSharedSection() { breadcrumb?.classList.add('hidden'); // Hide actions-bar for shared view - window.setActionsBarMode('hidden'); + setActionsBarMode('hidden'); //reset files view + remove any error - window.ui.resetFilesList(); + ui.resetFilesList(); // Hide file containers toggleFileContainer(false); // Show shared view - if (window.sharedView) { - window.sharedView.init(); - window.sharedView.show(); + if (sharedView) { + sharedView.init(); + sharedView.show(); } - if (window.multiSelect) window.multiSelect.clear(); + if (multiSelect) multiSelect.clear(); } function switchToFilesSection() { if (!setCurrentSection('files')) return; // Set actions bar mode - window.setActionsBarMode('files', true); + setActionsBarMode('files', true); // Show breadcrumb (only in Files view) const breadcrumb = document.querySelector('.breadcrumb'); @@ -208,22 +214,22 @@ function switchToFilesSection() { syncViewContainers(); //reset files view + remove any error - window.ui.resetFilesList(); + ui.resetFilesList(); // Reset to home folder and update breadcrumb - window.app.currentPath = window.app.userHomeFolderId || ''; - window.app.breadcrumbPath = []; - window.ui.updateBreadcrumb(); - if (window.multiSelect) window.multiSelect.clear(); + app.currentPath = app.userHomeFolderId || ''; + app.breadcrumbPath = []; + ui.updateBreadcrumb(); + if (multiSelect) multiSelect.clear(); - window.loadFiles(); + loadFiles(); } function switchToFavoritesSection() { if (!setCurrentSection('favorites')) return; // Set actions bar mode - window.setActionsBarMode('favorites'); + setActionsBarMode('favorites'); // Hide breadcrumb (only shown in Files view) const breadcrumb = document.querySelector('.breadcrumb'); @@ -236,26 +242,26 @@ function switchToFavoritesSection() { syncViewContainers(); //reset files view + remove any error - window.ui.resetFilesList(); + ui.resetFilesList(); - if (window.favorites) { - window.favorites.displayFavorites(); + if (favorites) { + favorites.displayFavorites(); } else { console.error('Favorites module not loaded or initialized'); - window.ui.showError(` + ui.showError(`Error loading the favorites module
`); } - if (window.multiSelect) window.multiSelect.clear(); + if (multiSelect) multiSelect.clear(); } function switchToRecentFilesSection() { if (!setCurrentSection('recent')) return; // Set actions bar mode - window.setActionsBarMode('recent'); + setActionsBarMode('recent'); // Hide breadcrumb (only shown in Files view) const breadcrumb = document.querySelector('.breadcrumb'); @@ -268,18 +274,18 @@ function switchToRecentFilesSection() { syncViewContainers(); //reset files view + remove any error - window.ui.resetFilesList(); + ui.resetFilesList(); - if (window.recent) { - window.recent.displayRecentFiles(); + if (recent) { + recent.displayRecentFiles(); } else { console.error('Recent files module not loaded or initialized'); - window.ui.showError(` + ui.showError(`Error loading the recent module
`); } - if (window.multiSelect) window.multiSelect.clear(); + if (multiSelect) multiSelect.clear(); } function switchToPhotosSection() { @@ -290,19 +296,19 @@ function switchToPhotosSection() { breadcrumb?.classList.add('hidden'); // Hide actions-bar (photos has its own upload via selection bar) - window.setActionsBarMode('hidden'); + setActionsBarMode('hidden'); //reset files view + remove any error - window.ui.resetFilesList(); + ui.resetFilesList(); // Hide file containers toggleFileContainer(false); // Show photos view - if (window.photosView) { - window.photosView.show(); + if (photosView) { + photosView.show(); } - if (window.multiSelect) window.multiSelect.clear(); + if (multiSelect) multiSelect.clear(); } function switchToTrashSection() { @@ -319,15 +325,15 @@ function switchToTrashSection() { setActionsBarMode('trash'); //reset files view + remove any error - window.ui.resetFilesList(); + ui.resetFilesList(); //ensure buttons match the current view syncViewContainers(); // Load trash items - window.loadTrashItems(); + loadTrashItems(); - if (window.multiSelect) window.multiSelect.clear(); + if (multiSelect) multiSelect.clear(); } function switchToMusicSection() { @@ -341,27 +347,29 @@ function switchToMusicSection() { toggleFileContainer(false); // Hide actions-bar - window.setActionsBarMode('hidden'); + setActionsBarMode('hidden'); // Reset files view + remove any error - window.ui.resetFilesList(); + ui.resetFilesList(); // Hide list header (created by resetFilesList) const listHeader = document.querySelector('.list-header'); listHeader?.classList.add('hidden'); // Show music view - if (window.musicView) { - window.musicView.show(); + if (musicView) { + musicView.show(); } - if (window.multiSelect) window.multiSelect.clear(); + if (multiSelect) multiSelect.clear(); } -window.switchToFilesSection = switchToFilesSection; -window.switchToSharedSection = switchToSharedSection; -window.switchToFavoritesSection = switchToFavoritesSection; -window.switchToRecentFilesSection = switchToRecentFilesSection; -window.switchToPhotosSection = switchToPhotosSection; -window.switchToTrashSection = switchToTrashSection; -window.switchToMusicSection = switchToMusicSection; -window.syncViewContainers = syncViewContainers; +export { + switchToFavoritesSection, + switchToFilesSection, + switchToMusicSection, + switchToPhotosSection, + switchToRecentFilesSection, + switchToSharedSection, + switchToTrashSection, + syncViewContainers +}; diff --git a/static/js/app/searchView.js b/static/js/app/searchView.js index 2dfcfc85..2ace946f 100644 --- a/static/js/app/searchView.js +++ b/static/js/app/searchView.js @@ -2,16 +2,23 @@ * Search view orchestration logic */ -async function performSearch(query, sortBy) { - const app = window.app; +import { search } from '../features/files/search.js'; +import { resolveHomeFolder } from './authSession.js'; +import { app } from './state.js'; +import { ui } from './ui.js'; +/** + * @param {string} query + * @param {string} [sortBy] + */ +async function performSearch(query, sortBy) { console.log(`Performing search for: "${query}" (sort: ${sortBy || 'relevance'})`); try { app.isSearchMode = true; - window.ui.updateBreadcrumb(`Search: "${query}"`); + ui.updateBreadcrumb(`Search: "${query}"`); - window.ui.showError(`${window.i18n ? window.i18n.t('trash.empty_state') : 'The trash is empty'}
+${i18n ? i18n.t('trash.empty_state') : 'The trash is empty'}
`); return; } @@ -36,33 +43,27 @@ async function loadTrashItems() { }); } catch (error) { console.error('Error loading trash items:', error); - window.ui.showNotification('Error', 'Error loading trash items'); + ui.showNotification('Error', 'Error loading trash items'); } } function addTrashItemToView(item) { - const elements = window.appElements; + const elements = appElements; const isFile = item.item_type === 'file'; - const formattedDate = window.formatDateTime(item.trashed_at); + const formattedDate = formatDateTime(item.trashed_at); let iconClass; let typeLabel; let iconSpecialClass = ''; if (!isFile) { iconClass = item.icon_class || 'fas fa-folder'; - typeLabel = window.i18n ? window.i18n.t('files.file_types.folder') : 'Folder'; + typeLabel = i18n ? i18n.t('files.file_types.folder') : 'Folder'; } else { - iconClass = item.icon_class || (window.ui?.getIconClass ? window.ui.getIconClass(item.name) : 'fas fa-file'); - iconSpecialClass = window.ui?.getIconSpecialClass ? window.ui.getIconSpecialClass(item.name) : ''; + iconClass = item.icon_class || (ui?.getIconClass ? ui.getIconClass(item.name) : 'fas fa-file'); + iconSpecialClass = ui?.getIconSpecialClass ? ui.getIconSpecialClass(item.name) : ''; const cat = item.category || ''; - typeLabel = cat - ? window.i18n - ? window.i18n.t(`files.file_types.${cat.toLowerCase()}`) || cat - : cat - : window.i18n - ? window.i18n.t('files.file_types.document') - : 'Document'; + typeLabel = cat ? (i18n ? i18n.t(`files.file_types.${cat.toLowerCase()}`) || cat : cat) : i18n ? i18n.t('files.file_types.document') : 'Document'; } const isFolder = !isFile; @@ -85,10 +86,10 @@ function addTrashItemToView(item) {