feat(ui): add section 'Shared with me'
This commit is contained in:
@@ -42,7 +42,17 @@ async function getFolder(id) {
|
||||
}
|
||||
|
||||
/**
|
||||
* rebuild breadcrumb from selected folder (iterate up to root)
|
||||
* Rebuild breadcrumb from selected folder (iterate up to root).
|
||||
*
|
||||
* Stops traversal gracefully when a parent folder is not accessible
|
||||
* (e.g. the user entered via a "Shared with me" grant whose parent
|
||||
* folder they have no permission on). In that case the partial
|
||||
* breadcrumb built so far is kept — the deepest reachable ancestor
|
||||
* acts as the visual root, matching how Google Drive / Dropbox handle
|
||||
* shared subtrees.
|
||||
*
|
||||
* An error on the *target folder itself* (first iteration) is still
|
||||
* treated as a real error and redirects to the home folder.
|
||||
*/
|
||||
async function rebuildBreadCrumb() {
|
||||
/**
|
||||
@@ -80,12 +90,19 @@ async function rebuildBreadCrumb() {
|
||||
// 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
|
||||
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;
|
||||
if (id) app.currentPath = id;
|
||||
if (currentFolderInfo === null) {
|
||||
// Failed on the target folder itself — real error, fall back to home.
|
||||
console.warn(`Cannot access target folder ${app.currentPath}, falling back to home`);
|
||||
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;
|
||||
if (id) app.currentPath = id;
|
||||
} else {
|
||||
// Failed on a parent — hit the permission boundary of a shared subtree.
|
||||
// Stop traversal; the partial breadcrumb is the best we can show.
|
||||
console.log(`Stopped breadcrumb traversal at permission boundary (parent of ${currentFolderInfo.id} is not accessible)`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-1
@@ -16,10 +16,12 @@ 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 { grants } from '../model/grants.js';
|
||||
import { sharedView } from '../views/shared/sharedView.js';
|
||||
import { checkAuthentication } from './authSession.js';
|
||||
import { loadFiles } from './filesView.js';
|
||||
import {
|
||||
activateFilesUI,
|
||||
SECTIONS_MAPPER,
|
||||
switchToFavoritesSection,
|
||||
switchToFilesSection,
|
||||
@@ -27,6 +29,7 @@ import {
|
||||
switchToPhotosSection,
|
||||
switchToRecentFilesSection,
|
||||
switchToSharedSection,
|
||||
switchToSharedWithMeSection,
|
||||
switchToTrashSection
|
||||
} from './navigation.js';
|
||||
import { performSearch } from './searchView.js';
|
||||
@@ -140,12 +143,16 @@ const ACTIONS_BAR_TEMPLATES = {
|
||||
</div>
|
||||
${_multiSelectButons}
|
||||
${_toggleButtons}
|
||||
`,
|
||||
sharedwithme: `
|
||||
<div class="action-buttons" id="default-buttons"></div>
|
||||
${_toggleButtons}
|
||||
`
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {'files' | 'trash' | 'favorites' | 'recent' | 'hidden'} mode
|
||||
* @param {'files' | 'trash' | 'favorites' | 'recent' | 'sharedwithme' | 'hidden'} mode
|
||||
* @param {boolean} [force=false]
|
||||
* @returns
|
||||
*/
|
||||
@@ -398,6 +405,9 @@ function initApp() {
|
||||
app.viewFile = hashContext.file;
|
||||
}
|
||||
|
||||
// get grants (xxx: async methods)
|
||||
await grants.fetchIncomingGrants();
|
||||
await grants.fetchOutgoingGrants();
|
||||
loadFiles();
|
||||
}
|
||||
});
|
||||
@@ -652,6 +662,10 @@ function setupEventListeners() {
|
||||
switchToSharedSection();
|
||||
break;
|
||||
|
||||
case 'nav.sharedwithme':
|
||||
switchToSharedWithMeSection();
|
||||
break;
|
||||
|
||||
case 'nav.favorites':
|
||||
// Switch to favorites view
|
||||
switchToFavoritesSection();
|
||||
@@ -723,6 +737,12 @@ function setupEventListeners() {
|
||||
* @param {string} name
|
||||
*/
|
||||
export function selectFolder(id, name) {
|
||||
// When entering from a non-files section (e.g. "Shared with me"),
|
||||
// activate the Files UI (nav active state, breadcrumb, action bar,
|
||||
// container) without resetting the current path.
|
||||
if (app.currentSection !== 'files') {
|
||||
activateFilesUI();
|
||||
}
|
||||
app.breadcrumbPath.push({ id, name });
|
||||
app.currentPath = id;
|
||||
ui.updateBreadcrumb();
|
||||
|
||||
@@ -10,6 +10,7 @@ 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 { sharedWithMeView } from '../views/sharedWithMe/sharedWithMeView.js';
|
||||
import { loadFiles } from './filesView.js';
|
||||
import { setActionsBarMode } from './main.js';
|
||||
import { app, appElements } from './state.js';
|
||||
@@ -122,6 +123,7 @@ function getSectionFromNavItem(navItem) {
|
||||
export const SECTIONS_MAPPER = {
|
||||
files: switchToFilesSection,
|
||||
shared: switchToSharedSection,
|
||||
sharedwithme: switchToSharedWithMeSection,
|
||||
recent: switchToRecentFilesSection,
|
||||
favorites: switchToFavoritesSection,
|
||||
trash: switchToTrashSection,
|
||||
@@ -157,6 +159,11 @@ function setCurrentSection(section) {
|
||||
sharedView.hide();
|
||||
}
|
||||
|
||||
// Hide "Load more" button when leaving the sharedwithme section
|
||||
if (section !== 'sharedwithme' && sharedWithMeView) {
|
||||
sharedWithMeView.hide();
|
||||
}
|
||||
|
||||
// Hide photosView when switching to any other section
|
||||
if (section !== 'photos' && photosView) {
|
||||
photosView.hide();
|
||||
@@ -194,6 +201,26 @@ function switchToSharedSection() {
|
||||
if (multiSelect) multiSelect.clear();
|
||||
}
|
||||
|
||||
function switchToSharedWithMeSection() {
|
||||
if (!setCurrentSection('sharedwithme')) return;
|
||||
|
||||
// Hide breadcrumb (only shown in Files view)
|
||||
const breadcrumb = document.querySelector('.breadcrumb');
|
||||
breadcrumb?.classList.add('hidden');
|
||||
|
||||
// Show actions-bar with view toggle (no upload / new-folder in this view)
|
||||
setActionsBarMode('sharedwithme');
|
||||
|
||||
// Show the standard files container and respect grid/list preference
|
||||
toggleFileContainer(true);
|
||||
syncViewContainers();
|
||||
|
||||
if (multiSelect) multiSelect.clear();
|
||||
|
||||
// Load and render items into the files container
|
||||
sharedWithMeView.init();
|
||||
}
|
||||
|
||||
function switchToFilesSection() {
|
||||
if (!setCurrentSection('files')) return;
|
||||
|
||||
@@ -368,13 +395,34 @@ function switchToMusicSection() {
|
||||
if (multiSelect) multiSelect.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the Files section UI (nav state, breadcrumb, actions bar,
|
||||
* files container, grid/list sync) WITHOUT resetting `app.currentPath`
|
||||
* or `app.breadcrumbPath`.
|
||||
*
|
||||
* Used by `selectFolder` when the user clicks a folder from a
|
||||
* non-files section (e.g. "Shared with me") so the Files view is
|
||||
* fully set up before the folder content loads.
|
||||
*/
|
||||
function activateFilesUI() {
|
||||
setCurrentSection('files');
|
||||
setActionsBarMode('files', true);
|
||||
const breadcrumb = document.querySelector('.breadcrumb');
|
||||
breadcrumb?.classList.remove('hidden');
|
||||
toggleFileContainer(true);
|
||||
syncViewContainers();
|
||||
if (multiSelect) multiSelect.clear();
|
||||
}
|
||||
|
||||
export {
|
||||
activateFilesUI,
|
||||
switchToFavoritesSection,
|
||||
switchToFilesSection,
|
||||
switchToMusicSection,
|
||||
switchToPhotosSection,
|
||||
switchToRecentFilesSection,
|
||||
switchToSharedSection,
|
||||
switchToSharedWithMeSection,
|
||||
switchToTrashSection,
|
||||
syncViewContainers
|
||||
};
|
||||
|
||||
+15
-9
@@ -17,10 +17,10 @@ import { favorites } from '../features/library/favorites.js';
|
||||
import { recent } from '../features/library/recent.js';
|
||||
import { fileSharing } from '../features/sharing/fileSharing.js';
|
||||
import { thumbnail } from '../features/thumbnail.js';
|
||||
import { sharedView } from '../views/shared/sharedView.js';
|
||||
import { grants } from '../model/grants.js';
|
||||
import { loadFiles } from './filesView.js';
|
||||
import { updateHistory } from './main.js';
|
||||
import { switchToFilesSection, syncViewContainers } from './navigation.js';
|
||||
import { activateFilesUI, switchToFilesSection, syncViewContainers } from './navigation.js';
|
||||
import { app } from './state.js';
|
||||
import { uiFileTypes } from './uiFileTypes.js';
|
||||
import { uiNotifications } from './uiNotifications.js';
|
||||
@@ -54,7 +54,7 @@ const ui = {
|
||||
<i class="fas fa-star"></i> <span data-i18n="actions.favorite">Add to favorites</span>
|
||||
</div>
|
||||
<div class="context-menu-item" id="share-folder-option">
|
||||
<i class="fas fa-share-alt"></i> <span data-i18n="actions.share">Share</span>
|
||||
<i class="fas fa-oxiexport"></i> <span data-i18n="actions.share">Share</span>
|
||||
</div>
|
||||
<div class="context-menu-separator"></div>
|
||||
<div class="context-menu-item" id="rename-folder-option">
|
||||
@@ -98,7 +98,7 @@ const ui = {
|
||||
<i class="fas fa-star"></i> <span data-i18n="actions.favorite">Add to favorites</span>
|
||||
</div>
|
||||
<div class="context-menu-item" id="share-file-option">
|
||||
<i class="fas fa-share-alt"></i> <span data-i18n="actions.share">Share</span>
|
||||
<i class="fas fa-oxiexport"></i> <span data-i18n="actions.share">Share</span>
|
||||
</div>
|
||||
<div class="context-menu-item hidden" id="add-to-playlist-option">
|
||||
<i class="fas fa-compact-disc"></i> <span data-i18n="music.add_to_playlist">Add to Playlist</span>
|
||||
@@ -154,7 +154,7 @@ const ui = {
|
||||
shareDialog.innerHTML = `
|
||||
<div class="share-dialog-content">
|
||||
<div class="share-dialog-header">
|
||||
<i class="fas fa-share-alt dialog-header-icon"></i>
|
||||
<i class="fas fa-oxiexport dialog-header-icon"></i>
|
||||
<span data-i18n="dialogs.share_file">Share file</span>
|
||||
</div>
|
||||
<div class="shared-item-info">
|
||||
@@ -937,6 +937,11 @@ const ui = {
|
||||
loadFiles();
|
||||
return;
|
||||
}
|
||||
if (app.currentSection === 'sharedwithme') {
|
||||
// Activate Files UI (nav, breadcrumb, actions bar) without
|
||||
// resetting the path — the shared folder becomes the entry point.
|
||||
activateFilesUI();
|
||||
}
|
||||
app.breadcrumbPath.push({ id: folderId, name: folderName });
|
||||
app.currentPath = folderId;
|
||||
this.updateBreadcrumb();
|
||||
@@ -1334,7 +1339,7 @@ const ui = {
|
||||
if (folder.path) el.dataset.path = folder.path;
|
||||
|
||||
const isFav = favorites?.isFavorite(folder.id, 'folder');
|
||||
const isShared = sharedView.isShared(folder.id, 'folder');
|
||||
const isShared = grants.getOutgoingGrantsFor('folder', folder.id).length > 0; //sharedView.isShared(folder.id, 'folder');
|
||||
const formattedDate = formatDateTime(folder.modified_at);
|
||||
|
||||
el.innerHTML = `
|
||||
@@ -1345,7 +1350,7 @@ const ui = {
|
||||
</div>
|
||||
<span>${escapeHtml(folder.name)}</span>
|
||||
<div class="file-badge file-badge-favorite ${isFav ? '' : 'hidden'}"><i class="fas fa-star favorite-star-inline"></i></div>
|
||||
<div class="file-badge file-badge-shared ${isShared ? '' : 'hidden'}"><i class="fas fa-share-alt"></i></div>
|
||||
<div class="file-badge file-badge-shared ${isShared ? '' : 'hidden'}"><i class="fas fa-oxiexport"></i></div>
|
||||
</div>
|
||||
<div class="type-cell">${i18n.t('files.file_types.folder')}</div>
|
||||
<div class="size-cell">--</div>
|
||||
@@ -1377,7 +1382,8 @@ const ui = {
|
||||
const fileSize = file.size_formatted || formatFileSize(file.size);
|
||||
const formattedDate = formatDateTime(file.modified_at);
|
||||
const isFav = favorites?.isFavorite(file.id, 'file');
|
||||
const isShared = sharedView.isShared(file.id, 'file');
|
||||
const isShared = grants.getOutgoingGrantsFor('file', file.id).length > 0;
|
||||
//const isShared = sharedView.isShared(file.id, 'file');
|
||||
const canThumbnail = thumbnail.canHandle(file);
|
||||
|
||||
const el = document.createElement('div');
|
||||
@@ -1398,7 +1404,7 @@ const ui = {
|
||||
</div>
|
||||
<span>${escapeHtml(file.name)}</span>
|
||||
<div class="file-badge file-badge-favorite ${isFav ? '' : 'hidden'}"><i class="fas fa-star favorite-star-inline"></i></div>
|
||||
<div class="file-badge file-badge-shared ${isShared ? '' : 'hidden'}"><i class="fas fa-share-alt"></i></div>
|
||||
<div class="file-badge file-badge-shared ${isShared ? '' : 'hidden'}"><i class="fas fa-oxiexport"></i></div>
|
||||
</div>
|
||||
<div class="type-cell">${typeLabel}</div>
|
||||
<div class="size-cell">${fileSize}</div>
|
||||
|
||||
Reference in New Issue
Block a user