feat(navigation) add browser history: permits url bookmarking on current folder
This commit is contained in:
@@ -119,9 +119,8 @@ async function checkAuthentication() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await resolveHomeFolder();
|
await resolveHomeFolder();
|
||||||
window.loadFiles();
|
window.dispatchEvent(new Event('authenticationDone'));
|
||||||
} else {
|
} else {
|
||||||
// No cached user data — must verify session from server
|
// No cached user data — must verify session from server
|
||||||
console.log('No cached user data, fetching from server');
|
console.log('No cached user data, fetching from server');
|
||||||
|
|||||||
+121
-2
@@ -1,8 +1,115 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
// TODO move to features/files/fileOperations.js ?
|
||||||
/**
|
/**
|
||||||
* Files view loading logic
|
* @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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function loadFiles(options = {}) {
|
/**
|
||||||
|
* getFolder information
|
||||||
|
* @param {string} id the id of the folder
|
||||||
|
* @returns {Promise<FolderInfo>}
|
||||||
|
*/
|
||||||
|
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 app = window.app;
|
||||||
const elements = window.appElements;
|
const elements = window.appElements;
|
||||||
|
|
||||||
@@ -30,6 +137,14 @@ async function loadFiles(options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const timestamp = new Date().getTime();
|
const timestamp = new Date().getTime();
|
||||||
|
|
||||||
|
await rebuildBreadCrumb();
|
||||||
|
|
||||||
|
// request a breadcrumb paint
|
||||||
|
window.ui.updateBreadcrumb();
|
||||||
|
|
||||||
|
window.updateHistory( options.insertHistory || false);
|
||||||
|
|
||||||
let url;
|
let url;
|
||||||
|
|
||||||
if (!app.currentPath || app.currentPath === '') {
|
if (!app.currentPath || app.currentPath === '') {
|
||||||
@@ -48,10 +163,13 @@ async function loadFiles(options = {}) {
|
|||||||
console.log(`Loading subfolder content: ${app.currentPath}`);
|
console.log(`Loading subfolder content: ${app.currentPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {HeadersInit} */
|
||||||
const headers = {
|
const headers = {
|
||||||
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
||||||
'Pragma': 'no-cache'
|
'Pragma': 'no-cache'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @type {RequestInit} */
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
headers,
|
headers,
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
@@ -60,6 +178,7 @@ async function loadFiles(options = {}) {
|
|||||||
|
|
||||||
if (forceRefresh) {
|
if (forceRefresh) {
|
||||||
url += `&force_refresh=true`;
|
url += `&force_refresh=true`;
|
||||||
|
// @ts-ignore
|
||||||
requestOptions.headers['X-Force-Refresh'] = 'true';
|
requestOptions.headers['X-Force-Refresh'] = 'true';
|
||||||
console.log('Forcing complete refresh ignoring cache');
|
console.log('Forcing complete refresh ignoring cache');
|
||||||
}
|
}
|
||||||
|
|||||||
+190
-35
@@ -3,11 +3,16 @@
|
|||||||
* This file contains the core functionality, initialization and state management
|
* This file contains the core functionality, initialization and state management
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
const app = window.app;
|
const app = window.app;
|
||||||
const elements = window.appElements;
|
const elements = window.appElements;
|
||||||
|
|
||||||
// Upload dropdown listener state (prevents accumulated listeners)
|
// Upload dropdown listener state (prevents accumulated listeners)
|
||||||
|
/** @type { function | null } */
|
||||||
let uploadDropdownDocumentClickHandler = null;
|
let uploadDropdownDocumentClickHandler = null;
|
||||||
|
|
||||||
|
/** @type { AbortController | null } */
|
||||||
let uploadDropdownBindingsController = null;
|
let uploadDropdownBindingsController = null;
|
||||||
let actionsBarDelegationBound = false;
|
let actionsBarDelegationBound = false;
|
||||||
|
|
||||||
@@ -90,6 +95,12 @@ const ACTIONS_BAR_TEMPLATES = {
|
|||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} mode
|
||||||
|
* @param {boolean} [force=false]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
function setActionsBarMode(mode, force = false) {
|
function setActionsBarMode(mode, force = false) {
|
||||||
if (!elements.actionsBar) return;
|
if (!elements.actionsBar) return;
|
||||||
|
|
||||||
@@ -180,6 +191,126 @@ function setupActionsBarDelegation() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} OxiContext
|
||||||
|
* @property {string | null} path the uuid of the path
|
||||||
|
* @property {string} section
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the application hash
|
||||||
|
*
|
||||||
|
* format:
|
||||||
|
*
|
||||||
|
* #/<section>/
|
||||||
|
*
|
||||||
|
* #/shared
|
||||||
|
* #/recent
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* special case of drive:
|
||||||
|
*
|
||||||
|
* #/files/folder/<folder ID>
|
||||||
|
*
|
||||||
|
* @returns {OxiContext}
|
||||||
|
*/
|
||||||
|
function deserializeHash() {
|
||||||
|
let hashContext = /** type {OxiContext} */ {};
|
||||||
|
|
||||||
|
// FIXME rename files into drive ?
|
||||||
|
hashContext.section = 'files';
|
||||||
|
|
||||||
|
let hash_elements = window.location.hash.split("/");
|
||||||
|
|
||||||
|
let section = hash_elements[1];
|
||||||
|
|
||||||
|
// FIXME: use navigation.VIEW_FLAGS
|
||||||
|
if (section && ['files', 'shared', 'recent', 'favorites', 'trash', 'photos'].includes(section)) {
|
||||||
|
hashContext.section = section;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash_elements[1] == 'files' && hash_elements[2] == 'folder' && hash_elements[3] !== null) {
|
||||||
|
hashContext.path = hash_elements[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update borwser's url/history
|
||||||
|
*
|
||||||
|
* @param {boolean} insertHistory true to change url and browser's history, false to change url only
|
||||||
|
*/
|
||||||
|
function updateHistory( insertHistory) {
|
||||||
|
const app = window.app;
|
||||||
|
|
||||||
|
let historyData = {
|
||||||
|
section: app.currentSection,
|
||||||
|
id: app.currentFolder,
|
||||||
|
};
|
||||||
|
let historyUrl = `#/${app.currentSection}`;
|
||||||
|
|
||||||
|
if (app.currentSection === 'files' && app.currentFolderInfo !== null) {
|
||||||
|
historyData.id = app.currentFolder;
|
||||||
|
historyUrl = historyUrl.concat('/', app.currentFolderInfo.id);
|
||||||
|
|
||||||
|
// update title
|
||||||
|
document.title = `OxiCloud: ${app.currentFolderInfo.path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertHistory) {
|
||||||
|
console.log(`adding history with ${historyUrl}`)
|
||||||
|
window.history.pushState(historyData, "", historyUrl);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(`replace history with ${historyUrl}`)
|
||||||
|
window.history.replaceState(historyData, "", historyUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} section
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function switchSectionTo(section) {
|
||||||
|
|
||||||
|
if (window.app.currentSection === section)
|
||||||
|
// no change ...
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (section) {
|
||||||
|
case "files":
|
||||||
|
switchToFilesView();
|
||||||
|
|
||||||
|
case "shared":
|
||||||
|
switchToSharedView();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "recent":
|
||||||
|
switchToRecentFilesView();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "favorites":
|
||||||
|
switchToFavoritesView();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "photos":
|
||||||
|
switchToPhotosView();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "trash":
|
||||||
|
switchToTrashView();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn(`context view ${section} unkonwn fallback to drive section`);
|
||||||
|
switchToFilesView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the application
|
* Initialize the application
|
||||||
*/
|
*/
|
||||||
@@ -233,6 +364,20 @@ function initApp() {
|
|||||||
window.multiSelect.init();
|
window.multiSelect.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.addEventListener('authenticationDone', () => {
|
||||||
|
// Check if a context was provided in the URL
|
||||||
|
let hashContext = deserializeHash();
|
||||||
|
switchSectionTo( hashContext.section);
|
||||||
|
if (hashContext.section === "files") {
|
||||||
|
if (hashContext.path) {
|
||||||
|
console.log(`init: reusing folder from hash URL: ${hashContext.path}`);
|
||||||
|
window.app.currentPath = hashContext.path;
|
||||||
|
}
|
||||||
|
window.loadFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
// Wait for translations to load before checking authentication
|
// Wait for translations to load before checking authentication
|
||||||
if (window.i18n && window.i18n.isLoaded && window.i18n.isLoaded()) {
|
if (window.i18n && window.i18n.isLoaded && window.i18n.isLoaded()) {
|
||||||
// Translations already loaded, proceed with authentication
|
// Translations already loaded, proceed with authentication
|
||||||
@@ -271,7 +416,6 @@ function cacheElements() {
|
|||||||
elements.pageTitle = document.querySelector('.page-title');
|
elements.pageTitle = document.querySelector('.page-title');
|
||||||
elements.actionsBar = document.querySelector('.actions-bar');
|
elements.actionsBar = document.querySelector('.actions-bar');
|
||||||
elements.navItems = document.querySelectorAll('.nav-item');
|
elements.navItems = document.querySelectorAll('.nav-item');
|
||||||
elements.trashBtn = document.querySelector('.nav-item:nth-child(6)'); // The trash nav item (after Photos)
|
|
||||||
elements.searchInput = document.querySelector('.search-container input');
|
elements.searchInput = document.querySelector('.search-container input');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,12 +450,14 @@ function setupUploadDropdown() {
|
|||||||
// Close dropdown when clicking outside
|
// Close dropdown when clicking outside
|
||||||
// remove+add stable handler: guarantees exactly one global listener
|
// remove+add stable handler: guarantees exactly one global listener
|
||||||
if (uploadDropdownDocumentClickHandler) {
|
if (uploadDropdownDocumentClickHandler) {
|
||||||
|
// @ts-ignore
|
||||||
document.removeEventListener('click', uploadDropdownDocumentClickHandler);
|
document.removeEventListener('click', uploadDropdownDocumentClickHandler);
|
||||||
}
|
}
|
||||||
uploadDropdownDocumentClickHandler = (e) => {
|
uploadDropdownDocumentClickHandler = (e) => {
|
||||||
if (e.target.closest('#upload-dropdown')) return;
|
if (e.target.closest('#upload-dropdown')) return;
|
||||||
document.querySelectorAll('.upload-dropdown-menu.show').forEach(m => m.classList.remove('show'));
|
document.querySelectorAll('.upload-dropdown-menu.show').forEach(m => m.classList.remove('show'));
|
||||||
};
|
};
|
||||||
|
// @ts-ignore
|
||||||
document.addEventListener('click', uploadDropdownDocumentClickHandler);
|
document.addEventListener('click', uploadDropdownDocumentClickHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,6 +473,25 @@ function setupEventListeners() {
|
|||||||
const SEARCH_DEBOUNCE_MS = 300;
|
const SEARCH_DEBOUNCE_MS = 300;
|
||||||
const SEARCH_MIN_CHARS = 3;
|
const SEARCH_MIN_CHARS = 3;
|
||||||
|
|
||||||
|
// handle history / url change
|
||||||
|
window.addEventListener("popstate", (e) => {
|
||||||
|
if (e.state === null) {
|
||||||
|
// change is from user (url explicitely change, read information from hash)
|
||||||
|
let hashContext = deserializeHash();
|
||||||
|
switchSectionTo( hashContext.section);
|
||||||
|
if (hashContext.path) {
|
||||||
|
window.app.currentPath = hashContext.path;
|
||||||
|
window.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});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Search input — Enter key
|
// Search input — Enter key
|
||||||
elements.searchInput.addEventListener('keydown', (e) => {
|
elements.searchInput.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
@@ -415,55 +580,44 @@ function setupEventListeners() {
|
|||||||
|
|
||||||
// Add active class to clicked item
|
// Add active class to clicked item
|
||||||
item.classList.add('active');
|
item.classList.add('active');
|
||||||
|
let _updateHistory = true;
|
||||||
|
|
||||||
// Check if this is the shared item
|
let itemI18nKey=item.querySelector('span').getAttribute('data-i18n')
|
||||||
if (item.querySelector('span').getAttribute('data-i18n') === 'nav.shared') {
|
switch(itemI18nKey) {
|
||||||
|
case 'nav.shared':
|
||||||
// Switch to shared view
|
// Switch to shared view
|
||||||
switchToSharedView();
|
switchToSharedView();
|
||||||
return;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is the favorites item
|
case 'nav.favorites':
|
||||||
if (item.querySelector('span').getAttribute('data-i18n') === 'nav.favorites') {
|
|
||||||
// Switch to favorites view
|
// Switch to favorites view
|
||||||
switchToFavoritesView();
|
switchToFavoritesView();
|
||||||
return;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is the recent files item
|
case 'nav.recent':
|
||||||
if (item.querySelector('span').getAttribute('data-i18n') === 'nav.recent') {
|
|
||||||
// Switch to recent files view
|
// Switch to recent files view
|
||||||
switchToRecentFilesView();
|
switchToRecentFilesView();
|
||||||
return;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is the photos item
|
case 'nav.photos':
|
||||||
if (item.querySelector('span').getAttribute('data-i18n') === 'nav.photos') {
|
|
||||||
switchToPhotosView();
|
switchToPhotosView();
|
||||||
return;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is the trash item
|
case 'nav.trash':
|
||||||
if (item === elements.trashBtn) {
|
switchToTrashView();
|
||||||
setCurrentSection('trash');
|
break;
|
||||||
|
|
||||||
// Hide breadcrumb (only shown in Files view)
|
default:
|
||||||
const breadcrumb = document.querySelector('.breadcrumb');
|
|
||||||
if (breadcrumb) breadcrumb.style.display = 'none';
|
|
||||||
|
|
||||||
// Show files containers (to be filled with trash)
|
|
||||||
const filesGrid = document.getElementById('files-grid');
|
|
||||||
const filesListView = document.getElementById('files-list-view');
|
|
||||||
if (filesGrid) { filesGrid.style.display = app.currentView === 'grid' ? 'grid' : 'none'; filesGrid.classList.toggle('hidden', app.currentView !== 'grid'); }
|
|
||||||
if (filesListView) { filesListView.style.display = app.currentView === 'list' ? 'flex' : 'none'; filesListView.classList.toggle('hidden', app.currentView !== 'list'); }
|
|
||||||
|
|
||||||
setActionsBarMode('trash');
|
|
||||||
|
|
||||||
// Load trash items
|
|
||||||
window.loadTrashItems();
|
|
||||||
} else {
|
|
||||||
// Use the proper switchToFilesView function which handles all UI restoration
|
// Use the proper switchToFilesView function which handles all UI restoration
|
||||||
window.switchToFilesView();
|
window.switchToFilesView();
|
||||||
|
// FIXME: because fileview handles it: need to converge code
|
||||||
|
_updateHistory = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.title = `OxiCloud: ${window.i18n.t(itemI18nKey)}`;
|
||||||
|
|
||||||
|
if (_updateHistory) {
|
||||||
|
updateHistory( true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -577,3 +731,4 @@ window.updateStorageUsageDisplay = updateStorageUsageDisplay;
|
|||||||
|
|
||||||
// Initialize app when DOM is ready
|
// Initialize app when DOM is ready
|
||||||
window.initApp = initApp;
|
window.initApp = initApp;
|
||||||
|
window.updateHistory = updateHistory;
|
||||||
|
|||||||
@@ -287,8 +287,28 @@ function switchToPhotosView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function switchToTrashView() {
|
||||||
|
setCurrentSection('trash');
|
||||||
|
|
||||||
|
// Hide breadcrumb (only shown in Files view)
|
||||||
|
const breadcrumb = document.querySelector('.breadcrumb');
|
||||||
|
if (breadcrumb) breadcrumb.style.display = 'none';
|
||||||
|
|
||||||
|
// Show files containers (to be filled with trash)
|
||||||
|
const filesGrid = document.getElementById('files-grid');
|
||||||
|
const filesListView = document.getElementById('files-list-view');
|
||||||
|
if (filesGrid) { filesGrid.style.display = app.currentView === 'grid' ? 'grid' : 'none'; filesGrid.classList.toggle('hidden', app.currentView !== 'grid'); }
|
||||||
|
if (filesListView) { filesListView.style.display = app.currentView === 'list' ? 'flex' : 'none'; filesListView.classList.toggle('hidden', app.currentView !== 'list'); }
|
||||||
|
|
||||||
|
setActionsBarMode('trash');
|
||||||
|
|
||||||
|
// Load trash items
|
||||||
|
window.loadTrashItems();
|
||||||
|
}
|
||||||
|
|
||||||
window.switchToFilesView = switchToFilesView;
|
window.switchToFilesView = switchToFilesView;
|
||||||
window.switchToSharedView = switchToSharedView;
|
window.switchToSharedView = switchToSharedView;
|
||||||
window.switchToFavoritesView = switchToFavoritesView;
|
window.switchToFavoritesView = switchToFavoritesView;
|
||||||
window.switchToRecentFilesView = switchToRecentFilesView;
|
window.switchToRecentFilesView = switchToRecentFilesView;
|
||||||
window.switchToPhotosView = switchToPhotosView;
|
window.switchToPhotosView = switchToPhotosView;
|
||||||
|
window.switchToTrashView = switchToTrashView;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ window.app = {
|
|||||||
currentView: 'grid',
|
currentView: 'grid',
|
||||||
currentPath: '',
|
currentPath: '',
|
||||||
currentFolder: null,
|
currentFolder: null,
|
||||||
|
currentFolderInfo: null,
|
||||||
contextMenuTargetFolder: null,
|
contextMenuTargetFolder: null,
|
||||||
contextMenuTargetFile: null,
|
contextMenuTargetFile: null,
|
||||||
selectedTargetFolderId: '',
|
selectedTargetFolderId: '',
|
||||||
|
|||||||
Reference in New Issue
Block a user