Refine: virtual locations

- Refactor: Make drive list change reactively
This commit is contained in:
aleksey-hoffman committed 2026-06-30 16:31:47 +03:00
1 parent a6289a9d53
commit 0aa3d58dbc
3 files changed
+123 -15

No files matched your search

@@ -33,6 +33,9 @@ import {
getNavigableParentPath,
isVirtualLocationPath,
resolveDirectoryContents,
buildLocationsDirectoryFromDrives,
getLocationsDriveListDisplaySignature,
getLocationsEntriesDisplaySignature,
} from '@/utils/virtual-locations';
import { sharedDrives } from '@/modules/home/composables/use-drives';
import { shouldIncludeItemCountsForSort } from '@/modules/navigator/components/file-browser/utils/file-browser-sort-columns';
@@ -46,13 +49,6 @@ interface DirChangePayload {
const DIRECTORY_DWELL_TIME_MS = 3000;
const WATCHER_DEBOUNCE_MS = 500;
function getDrivePathListSignature(paths: string[]): string {
return paths
.map(path => normalizePath(path))
.sort()
.join('\0');
}
function dirWatcherDiagEnabled(): boolean {
return import.meta.env.DEV && localStorage.getItem('SFM_DIR_WATCHER_DIAG') === '1';
}
@@ -266,10 +262,10 @@ export function useFileBrowserNavigation(
return;
}
const displayedDrivePaths = dirContents.value.entries.map(entry => entry.path);
const liveDrivePaths = sharedDrives.value.map(drive => drive.path);
const displayedSignature = getLocationsEntriesDisplaySignature(dirContents.value.entries);
const liveSignature = getLocationsDriveListDisplaySignature(sharedDrives.value);
if (getDrivePathListSignature(displayedDrivePaths) === getDrivePathListSignature(liveDrivePaths)) {
if (displayedSignature === liveSignature) {
return;
}
@@ -290,14 +286,22 @@ export function useFileBrowserNavigation(
const canGoForward = computed(() => historyIndex.value < history.value.length - 1);
async function silentRefresh(): Promise<void> {
if (!currentPath.value) {
const refreshPath = currentPath.value;
if (!refreshPath) {
return;
}
isRefreshing.value = true;
try {
const result = await loadDirectoryContents(currentPath.value, createReadDirOptions());
const result = isVirtualLocationPath(refreshPath)
? buildLocationsDirectoryFromDrives(sharedDrives.value)
: await loadDirectoryContents(refreshPath, createReadDirOptions());
if (currentPath.value !== refreshPath) {
return;
}
dirContents.value = result;
invalidateDirectoryLinkMetadata(result);
@@ -319,6 +323,10 @@ export function useFileBrowserNavigation(
}
}
catch {
if (currentPath.value !== refreshPath) {
return;
}
await navigateToNearestExistingAncestor();
}
finally {
@@ -5,8 +5,11 @@
import { describe, expect, it } from 'vitest';
import {
LOCATIONS_VIRTUAL_PATH,
buildLocationsDirectoryFromDrives,
createLocationsDirEntry,
driveInfoToDirEntry,
getLocationsDriveListDisplaySignature,
getLocationsEntriesDisplaySignature,
getNavigableParentPath,
getVirtualLocationDisplayName,
isVirtualLocationPath,
@@ -60,6 +63,57 @@ describe('virtual-locations', () => {
expect(driveEntry.item_count).toBeNull();
});
it('builds locations directory contents from drive snapshots', () => {
const drivePayload: DriveInfo = {
name: 'Local Disk (C:)',
path: 'C:/',
mount_point: 'C:\\',
file_system: 'NTFS',
drive_type: 'SSD',
total_space: 1000,
available_space: 500,
used_space: 500,
percent_used: 50,
is_removable: false,
is_read_only: false,
is_mounted: true,
device_path: '\\\\.\\C:',
};
const contents = buildLocationsDirectoryFromDrives([drivePayload]);
expect(contents.path).toBe(LOCATIONS_VIRTUAL_PATH);
expect(contents.entries).toHaveLength(1);
expect(contents.entries[0]?.path).toBe('C:/');
});
it('detects drive metadata changes in locations display signatures', () => {
const baseDrive: DriveInfo = {
name: 'Local Disk (C:)',
path: 'C:/',
mount_point: 'C:\\',
file_system: 'NTFS',
drive_type: 'SSD',
total_space: 1000,
available_space: 500,
used_space: 500,
percent_used: 50,
is_removable: false,
is_read_only: false,
is_mounted: true,
device_path: '\\\\.\\C:',
};
const renamedDrive = { ...baseDrive, name: 'System (C:)' };
const resizedDrive = { ...baseDrive, total_space: 2000 };
const entries = buildLocationsDirectoryFromDrives([baseDrive]).entries;
const baseSignature = getLocationsDriveListDisplaySignature([baseDrive]);
expect(getLocationsEntriesDisplaySignature(entries)).toBe(baseSignature);
expect(getLocationsDriveListDisplaySignature([renamedDrive])).not.toBe(baseSignature);
expect(getLocationsDriveListDisplaySignature([resizedDrive])).not.toBe(baseSignature);
});
describe('getNavigableParentPath', () => {
it('returns null for the virtual locations path', () => {
expect(getNavigableParentPath('sfm://locations', 'windows')).toBeNull();
+49 -3
View File
@@ -4,7 +4,7 @@
import { invoke } from '@tauri-apps/api/core';
import type { DirContents, DirEntry, ReadDirOptions } from '@/types/dir-entry';
import type { DriveInfo } from '@/types/drive-info';
import type { DriveEntryMetadata, DriveInfo } from '@/types/drive-info';
import normalizePath, {
getParentPath,
getPathDisplayName,
@@ -74,8 +74,7 @@ export function createLocationsDirEntry(): DirEntry {
};
}
export async function readLocationsDirectory(): Promise<DirContents> {
const drives = await invoke<DriveInfo[]>('get_system_drives');
export function buildLocationsDirectoryFromDrives(drives: DriveInfo[]): DirContents {
const entries = drives.map(drive => driveInfoToDirEntry(drive));
const directoryCount = entries.length;
@@ -93,6 +92,53 @@ export async function readLocationsDirectory(): Promise<DirContents> {
};
}
function formatLocationsDriveDisplaySignaturePart(
path: string,
name: string,
size: number,
driveMetadata: DriveEntryMetadata | undefined,
): string {
return [
normalizePath(path),
name,
String(size),
driveMetadata?.drive_type ?? '',
driveMetadata?.is_removable ? '1' : '0',
].join('\x1f');
}
export function getLocationsDriveListDisplaySignature(drives: DriveInfo[]): string {
return drives
.map((drive) => {
const entry = driveInfoToDirEntry(drive);
return formatLocationsDriveDisplaySignaturePart(
entry.path,
entry.name,
entry.size,
entry.drive_metadata,
);
})
.sort()
.join('\0');
}
export function getLocationsEntriesDisplaySignature(entries: DirEntry[]): string {
return entries
.map(entry => formatLocationsDriveDisplaySignaturePart(
entry.path,
entry.name,
entry.size,
entry.drive_metadata,
))
.sort()
.join('\0');
}
export async function readLocationsDirectory(): Promise<DirContents> {
const drives = await invoke<DriveInfo[]>('get_system_drives');
return buildLocationsDirectoryFromDrives(drives);
}
export async function resolveDirectoryContents(
path: string,
options?: ReadDirOptions,