(selected)
| 2498 | } |
| 2499 | |
| 2500 | async function selectFolder(selected) { |
| 2501 | const container = document.getElementById('folderTreeContainer'); |
| 2502 | if (!container) return; |
| 2503 | |
| 2504 | // If the node is in the tree, trust its locked class. |
| 2505 | let opt = container.querySelector(`.folder-option[data-folder="${CSS.escape(selected)}"]`); |
| 2506 | let allowed = true; |
| 2507 | applyFolderCapabilities(selected); |
| 2508 | if (opt && opt.classList.contains('locked')) { |
| 2509 | allowed = false; |
| 2510 | } else if (!opt) { |
| 2511 | // Not in DOM → preflight capabilities so breadcrumbs (and other callers) |
| 2512 | // can't jump into forbidden folders. |
| 2513 | try { |
| 2514 | allowed = await canViewFolder(selected); |
| 2515 | } catch (e) { |
| 2516 | allowed = false; |
| 2517 | } |
| 2518 | } |
| 2519 | |
| 2520 | if (!allowed) { |
| 2521 | showToast(t('no_access') || "You do not have access to this resource.", 'error'); |
| 2522 | return; // do NOT change currentFolder or lastOpenedFolder |
| 2523 | } |
| 2524 | |
| 2525 | // At this point we’re allowed. If the node isn’t visible yet, open its parents |
| 2526 | // so the tree reflects where we are going. |
| 2527 | if (!opt && selected && selected !== 'root') { |
| 2528 | const parts = selected.split('/').filter(Boolean); |
| 2529 | const st = loadFolderTreeState(); |
| 2530 | let acc = ''; |
| 2531 | for (let i = 0; i < parts.length; i++) { |
| 2532 | acc = i === 0 ? parts[i] : `${acc}/${parts[i]}`; |
| 2533 | st[acc] = 'block'; |
| 2534 | } |
| 2535 | saveFolderTreeState(st); |
| 2536 | // Materialize the opened branches |
| 2537 | await expandAndLoadSavedState(); |
| 2538 | opt = container.querySelector(`.folder-option[data-folder="${CSS.escape(selected)}"]`); |
| 2539 | } |
| 2540 | |
| 2541 | // Visual selection |
| 2542 | container.querySelectorAll(".folder-option").forEach(el => el.classList.remove("selected")); |
| 2543 | if (opt) opt.classList.add("selected"); |
| 2544 | |
| 2545 | // Update state + UI |
| 2546 | window.currentFolder = selected; |
| 2547 | setLastOpenedFolder(selected); |
| 2548 | updateBreadcrumbTitle(selected); |
| 2549 | applyFolderCapabilities(selected); |
| 2550 | ensureFolderIcon(selected); |
| 2551 | const skipCfg = window.__frSkipListReload || null; |
| 2552 | const activeSourceId = getActiveSourceId(); |
| 2553 | const shouldSkip = !!( |
| 2554 | skipCfg && |
| 2555 | skipCfg.folder === selected && |
| 2556 | (!skipCfg.sourceId || skipCfg.sourceId === activeSourceId) |
| 2557 | ); |
no test coverage detected