Files
aliasvault/apps/mobile-app/utils/FolderUtils.ts
dependabot[bot] 423330c864 Upgrade mobile app Expo and browser extension WXT frameworks (#2067)
* Bump the npm_and_yarn group across 2 directories with 2 updates

Bumps the npm_and_yarn group with 1 update in the /apps/browser-extension directory: [ws](https://github.com/websockets/ws).
Bumps the npm_and_yarn group with 1 update in the /apps/mobile-app directory: [postcss](https://github.com/postcss/postcss).


Updates `ws` from 8.18.2 to 8.21.0
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.18.2...8.21.0)

Updates `ws` from 8.18.2 to 8.21.0
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.18.2...8.21.0)

Updates `postcss` from 8.4.49 to 8.5.15
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.49...8.5.15)

Updates `postcss` from 8.4.49 to 8.5.15
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.49...8.5.15)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.15
  dependency-type: indirect
- dependency-name: ws
  dependency-version: 8.21.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update package-lock.json (#2067)

* Update NodeJS to latest 24.x LTS

* Revert Expo back to 53.x (#2067)

* Upgrade expo and react-native (#2067)

* Update mobile app to support Expo 56 (#2067)

* Update wxt (#2067)

* Change util filenames to be consistent PascalCase (#2067)

* Restore deep-linking behavior to work with new Expo Router (#2067)

* Update eslint.config.js (#2067)

* Update package-lock.json (#2067)

* Refactor linting (#2067)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Leendert de Borst <ldeborst@xivisoft.com>
2026-05-26 10:47:39 +02:00

181 lines
5.1 KiB
TypeScript

import type { Folder } from './db/repositories/FolderRepository';
/**
* Maximum allowed folder nesting depth.
* Structure: Root (0) > Level 1 (1) > Level 2 (2) > Level 3 (3) > Level 4 (4)
* Folders at depth 4 cannot have subfolders.
*/
export const MAX_FOLDER_DEPTH = 4;
/**
* Get folder depth in the hierarchy.
* @param folderId - The folder ID to check
* @param folders - Flat array of all folders
* @returns Depth (0 = root, 1 = one level deep, etc.) or null if folder not found
*/
export function getFolderDepth(folderId: string, folders: Folder[]): number | null {
const folder = folders.find(f => f.Id === folderId);
if (!folder) {
return null;
}
let depth = 0;
let currentId: string | null = folderId;
// Traverse up to root, counting levels
while (currentId) {
const current = folders.find(f => f.Id === currentId);
if (!current || !current.ParentFolderId) {
break;
}
depth++;
currentId = current.ParentFolderId;
// Prevent infinite loops
if (depth > MAX_FOLDER_DEPTH) {
break;
}
}
return depth;
}
/**
* Get the full path of folder names from root to the specified folder.
* @param folderId - The folder ID
* @param folders - Flat array of all folders
* @returns Array of folder names from root to current folder, or empty array if not found
*/
export function getFolderPath(folderId: string | null, folders: Folder[]): string[] {
if (!folderId) {
return [];
}
const path: string[] = [];
let currentId: string | null = folderId;
let iterations = 0;
// Build path by traversing up to root
while (currentId && iterations < MAX_FOLDER_DEPTH + 1) {
const folder = folders.find(f => f.Id === currentId);
if (!folder) {
break;
}
path.unshift(folder.Name); // Add to beginning of array
currentId = folder.ParentFolderId;
iterations++;
}
return path;
}
/**
* Get the full path of folder IDs from root to the specified folder.
* @param folderId - The folder ID
* @param folders - Flat array of all folders
* @returns Array of folder IDs from root to current folder, or empty array if not found
*/
export function getFolderIdPath(folderId: string | null, folders: Folder[]): string[] {
if (!folderId) {
return [];
}
const path: string[] = [];
let currentId: string | null = folderId;
let iterations = 0;
// Build path by traversing up to root
while (currentId && iterations < MAX_FOLDER_DEPTH + 1) {
const folder = folders.find(f => f.Id === currentId);
if (!folder) {
break;
}
path.unshift(folder.Id); // Add to beginning of array
currentId = folder.ParentFolderId;
iterations++;
}
return path;
}
/**
* Format folder path for display with separator.
* @param pathSegments - Array of folder names
* @param separator - Separator string (default: " > ")
* @returns Formatted folder path string
*/
export function formatFolderPath(
pathSegments: string[],
separator: string = ' > '
): string {
return pathSegments.join(separator);
}
/**
* Check if a folder can have subfolders (not at max depth).
* @param folderId - The folder ID to check
* @param folders - Flat array of all folders
* @returns True if folder can have children, false otherwise
*/
export function canHaveSubfolders(folderId: string, folders: Folder[]): boolean {
const depth = getFolderDepth(folderId, folders);
return depth !== null && depth < MAX_FOLDER_DEPTH;
}
/**
* Get all descendant folder IDs (children, grandchildren, etc.).
* @param folderId - The parent folder ID
* @param folders - Flat array of all folders
* @returns Array of descendant folder IDs
*/
export function getDescendantFolderIds(folderId: string, folders: Folder[]): string[] {
const descendants: string[] = [];
/**
* Traverse a folder tree and get all descendant folder IDs.
*/
const traverse = (parentId: string): void => {
folders
.filter(f => f.ParentFolderId === parentId)
.forEach(child => {
descendants.push(child.Id);
traverse(child.Id);
});
};
traverse(folderId);
return descendants;
}
/**
* Get all direct child folder IDs.
* @param parentFolderId - The parent folder ID (null for root)
* @param folders - Flat array of all folders
* @returns Array of direct child folder IDs
*/
export function getDirectChildFolderIds(parentFolderId: string | null, folders: Folder[]): string[] {
return folders
.filter(f => f.ParentFolderId === parentFolderId)
.map(f => f.Id);
}
/**
* Get total count of items in a folder and all its subfolders.
* @param folderId - The folder ID to count items for
* @param allItems - All items in the vault
* @param allFolders - All folders in the vault
* @returns Total item count including subfolders
*/
export function getRecursiveItemCount(
folderId: string,
allItems: Array<{ FolderId?: string | null }>,
allFolders: Folder[]
): number {
// Get all descendant folder IDs
const descendantIds = getDescendantFolderIds(folderId, allFolders);
const allFolderIds = [folderId, ...descendantIds];
// Count items in current folder and all descendants
return allItems.filter(item => item.FolderId && allFolderIds.includes(item.FolderId)).length;
}