|
@ -1,18 +1,10 @@ |
|
|
/** |
|
|
/** * Backup Files List Component * * Displays a list of backup files saved by |
|
|
* Backup Files List Component |
|
|
the app and provides options to: * - View backup files by type (contacts, seed, |
|
|
* |
|
|
other) * - Open individual files in the device's file viewer * - Access the |
|
|
* Displays a list of backup files saved by the app and provides options to: |
|
|
backup directory in the device's file explorer * * @component * @displayName |
|
|
* - View backup files by type (contacts, seed, other) |
|
|
BackupFilesList * @example * ```vue * |
|
|
* - Open individual files in the device's file viewer |
|
|
<BackupFilesList /> |
|
|
* - Access the backup directory in the device's file explorer |
|
|
* ``` */ |
|
|
* |
|
|
|
|
|
* @component |
|
|
|
|
|
* @displayName BackupFilesList |
|
|
|
|
|
* @example |
|
|
|
|
|
* ```vue |
|
|
|
|
|
* <BackupFilesList /> |
|
|
|
|
|
* ``` |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
<template> |
|
|
<template> |
|
|
<div class="backup-files-list"> |
|
|
<div class="backup-files-list"> |
|
@ -21,47 +13,47 @@ |
|
|
<div class="flex gap-2"> |
|
|
<div class="flex gap-2"> |
|
|
<button |
|
|
<button |
|
|
v-if="platformCapabilities.hasFileSystem" |
|
|
v-if="platformCapabilities.hasFileSystem" |
|
|
@click="refreshFiles()" |
|
|
|
|
|
class="text-sm bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded" |
|
|
class="text-sm bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded" |
|
|
:disabled="isLoading" |
|
|
:disabled="isLoading" |
|
|
|
|
|
@click="refreshFiles()" |
|
|
> |
|
|
> |
|
|
<font-awesome |
|
|
<font-awesome |
|
|
icon="refresh" |
|
|
icon="refresh" |
|
|
class="fa-fw" |
|
|
class="fa-fw" |
|
|
:class="{ 'animate-spin': isLoading }" |
|
|
:class="{ 'animate-spin': isLoading }" |
|
|
/> |
|
|
/> |
|
|
Refresh |
|
|
Refresh |
|
|
</button> |
|
|
</button> |
|
|
<button |
|
|
<button |
|
|
v-if="platformCapabilities.hasFileSystem" |
|
|
v-if="platformCapabilities.hasFileSystem" |
|
|
@click="openBackupDirectory()" |
|
|
|
|
|
class="text-sm bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded" |
|
|
class="text-sm bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded" |
|
|
:disabled="isLoading" |
|
|
:disabled="isLoading" |
|
|
|
|
|
@click="openBackupDirectory()" |
|
|
> |
|
|
> |
|
|
<font-awesome icon="folder-open" class="fa-fw" /> |
|
|
<font-awesome icon="folder-open" class="fa-fw" /> |
|
|
Open Directory |
|
|
Open Directory |
|
|
</button> |
|
|
</button> |
|
|
<button |
|
|
<button |
|
|
v-if="platformCapabilities.hasFileSystem && isDevelopment" |
|
|
v-if="platformCapabilities.hasFileSystem && isDevelopment" |
|
|
@click="debugFileDiscovery()" |
|
|
|
|
|
class="text-sm bg-yellow-500 hover:bg-yellow-600 text-white px-3 py-1 rounded" |
|
|
class="text-sm bg-yellow-500 hover:bg-yellow-600 text-white px-3 py-1 rounded" |
|
|
:disabled="isLoading" |
|
|
:disabled="isLoading" |
|
|
title="Debug file discovery (development only)" |
|
|
title="Debug file discovery (development only)" |
|
|
|
|
|
@click="debugFileDiscovery()" |
|
|
> |
|
|
> |
|
|
<font-awesome icon="bug" class="fa-fw" /> |
|
|
<font-awesome icon="bug" class="fa-fw" /> |
|
|
Debug |
|
|
Debug |
|
|
</button> |
|
|
</button> |
|
|
<button |
|
|
<button |
|
|
@click="createTestBackup" |
|
|
|
|
|
:disabled="isLoading" |
|
|
:disabled="isLoading" |
|
|
class="px-3 py-1 bg-green-500 text-white rounded text-sm hover:bg-green-600 disabled:opacity-50" |
|
|
class="px-3 py-1 bg-green-500 text-white rounded text-sm hover:bg-green-600 disabled:opacity-50" |
|
|
|
|
|
@click="createTestBackup" |
|
|
> |
|
|
> |
|
|
Create Test Backup |
|
|
Create Test Backup |
|
|
</button> |
|
|
</button> |
|
|
<button |
|
|
<button |
|
|
@click="testDirectoryContexts" |
|
|
|
|
|
:disabled="isLoading" |
|
|
:disabled="isLoading" |
|
|
class="px-3 py-1 bg-purple-500 text-white rounded text-sm hover:bg-purple-600 disabled:opacity-50" |
|
|
class="px-3 py-1 bg-purple-500 text-white rounded text-sm hover:bg-purple-600 disabled:opacity-50" |
|
|
|
|
|
@click="testDirectoryContexts" |
|
|
> |
|
|
> |
|
|
Test Contexts |
|
|
Test Contexts |
|
|
</button> |
|
|
</button> |
|
@ -73,21 +65,40 @@ |
|
|
<p class="mt-2">Loading backup files...</p> |
|
|
<p class="mt-2">Loading backup files...</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div v-else-if="backupFiles.length === 0" class="text-center py-4 text-gray-500"> |
|
|
<div |
|
|
|
|
|
v-else-if="backupFiles.length === 0" |
|
|
|
|
|
class="text-center py-4 text-gray-500" |
|
|
|
|
|
> |
|
|
<font-awesome icon="folder-open" class="fa-2x mb-2" /> |
|
|
<font-awesome icon="folder-open" class="fa-2x mb-2" /> |
|
|
<p>No backup files found</p> |
|
|
<p>No backup files found</p> |
|
|
<p class="text-sm mt-1">Create backups using the export functions above</p> |
|
|
<p class="text-sm mt-1"> |
|
|
<div class="mt-3 p-3 bg-blue-50 border border-blue-200 rounded-lg text-left"> |
|
|
Create backups using the export functions above |
|
|
<p class="text-sm font-medium text-blue-800 mb-2">💡 How to create backup files:</p> |
|
|
</p> |
|
|
|
|
|
<div |
|
|
|
|
|
class="mt-3 p-3 bg-blue-50 border border-blue-200 rounded-lg text-left" |
|
|
|
|
|
> |
|
|
|
|
|
<p class="text-sm font-medium text-blue-800 mb-2"> |
|
|
|
|
|
💡 How to create backup files: |
|
|
|
|
|
</p> |
|
|
<ul class="text-xs text-blue-700 space-y-1"> |
|
|
<ul class="text-xs text-blue-700 space-y-1"> |
|
|
<li>• Use the "Export Contacts" button above to create contact backups</li> |
|
|
<li> |
|
|
|
|
|
• Use the "Export Contacts" button above to create contact backups |
|
|
|
|
|
</li> |
|
|
<li>• Use the "Export Seed" button to backup your recovery phrase</li> |
|
|
<li>• Use the "Export Seed" button to backup your recovery phrase</li> |
|
|
<li>• Backup files are saved to persistent storage that survives app installations</li> |
|
|
<li> |
|
|
<li v-if="platformCapabilities.isMobile && !platformCapabilities.isIOS" class="text-orange-700"> |
|
|
• Backup files are saved to persistent storage that survives app |
|
|
• On Android: Files are saved to Downloads/TimeSafari or app data directory |
|
|
installations |
|
|
|
|
|
</li> |
|
|
|
|
|
<li |
|
|
|
|
|
v-if="platformCapabilities.isMobile && !platformCapabilities.isIOS" |
|
|
|
|
|
class="text-orange-700" |
|
|
|
|
|
> |
|
|
|
|
|
• On Android: Files are saved to Downloads/TimeSafari or app data |
|
|
|
|
|
directory |
|
|
</li> |
|
|
</li> |
|
|
<li v-if="platformCapabilities.isIOS" class="text-orange-700"> |
|
|
<li v-if="platformCapabilities.isIOS" class="text-orange-700"> |
|
|
• On iOS: Files are saved to Documents folder (accessible via Files app) |
|
|
• On iOS: Files are saved to Documents folder (accessible via Files |
|
|
|
|
|
app) |
|
|
</li> |
|
|
</li> |
|
|
</ul> |
|
|
</ul> |
|
|
</div> |
|
|
</div> |
|
@ -99,18 +110,20 @@ |
|
|
<button |
|
|
<button |
|
|
v-for="type in ['all', 'contacts', 'seed', 'other'] as const" |
|
|
v-for="type in ['all', 'contacts', 'seed', 'other'] as const" |
|
|
:key="type" |
|
|
:key="type" |
|
|
@click="selectedType = type" |
|
|
|
|
|
:class="[ |
|
|
:class="[ |
|
|
'text-sm px-3 py-1 rounded border', |
|
|
'text-sm px-3 py-1 rounded border', |
|
|
selectedType === type |
|
|
selectedType === type |
|
|
? 'bg-blue-500 text-white border-blue-500' |
|
|
? 'bg-blue-500 text-white border-blue-500' |
|
|
: 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50' |
|
|
: 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50', |
|
|
]" |
|
|
]" |
|
|
|
|
|
@click="selectedType = type" |
|
|
> |
|
|
> |
|
|
{{ type === 'all' ? 'All' : type.charAt(0).toUpperCase() + type.slice(1) }} |
|
|
{{ |
|
|
<span class="ml-1 text-xs"> |
|
|
type === "all" |
|
|
({{ getFileCountByType(type) }}) |
|
|
? "All" |
|
|
</span> |
|
|
: type.charAt(0).toUpperCase() + type.slice(1) |
|
|
|
|
|
}} |
|
|
|
|
|
<span class="ml-1 text-xs"> ({{ getFileCountByType(type) }}) </span> |
|
|
</button> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
@ -118,9 +131,9 @@ |
|
|
<div class="flex items-center gap-2 mb-2"> |
|
|
<div class="flex items-center gap-2 mb-2"> |
|
|
<span v-for="(crumb, idx) in breadcrumbs" :key="idx"> |
|
|
<span v-for="(crumb, idx) in breadcrumbs" :key="idx"> |
|
|
<span |
|
|
<span |
|
|
|
|
|
v-if="idx < breadcrumbs.length - 1" |
|
|
class="text-blue-600 cursor-pointer underline" |
|
|
class="text-blue-600 cursor-pointer underline" |
|
|
@click="goToBreadcrumb(idx)" |
|
|
@click="goToBreadcrumb(idx)" |
|
|
v-if="idx < breadcrumbs.length - 1" |
|
|
|
|
|
> |
|
|
> |
|
|
{{ crumb }} |
|
|
{{ crumb }} |
|
|
</span> |
|
|
</span> |
|
@ -129,14 +142,23 @@ |
|
|
</span> |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
<div v-if="currentPath.length > 1" class="mb-2"> |
|
|
<div v-if="currentPath.length > 1" class="mb-2"> |
|
|
<button @click="goUp" class="text-xs text-blue-500 underline">⬅ Up</button> |
|
|
<button class="text-xs text-blue-500 underline" @click="goUp"> |
|
|
|
|
|
⬅ Up |
|
|
|
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="mb-2"> |
|
|
<div class="mb-2"> |
|
|
<label class="inline-flex items-center"> |
|
|
<label class="inline-flex items-center"> |
|
|
<input type="checkbox" v-model="debugShowAll" @change="loadDirectory" class="mr-2" /> |
|
|
<input |
|
|
|
|
|
v-model="debugShowAll" |
|
|
|
|
|
type="checkbox" |
|
|
|
|
|
class="mr-2" |
|
|
|
|
|
@change="loadDirectory" |
|
|
|
|
|
/> |
|
|
<span class="text-xs">Debug: Show all entries as files</span> |
|
|
<span class="text-xs">Debug: Show all entries as files</span> |
|
|
</label> |
|
|
</label> |
|
|
<span v-if="debugShowAll" class="text-xs text-red-600 ml-2">[Debug mode: forcibly treating all entries as files]</span> |
|
|
<span v-if="debugShowAll" class="text-xs text-red-600 ml-2" |
|
|
|
|
|
>[Debug mode: forcibly treating all entries as files]</span |
|
|
|
|
|
> |
|
|
</div> |
|
|
</div> |
|
|
<div class="space-y-2 max-h-64 overflow-y-auto"> |
|
|
<div class="space-y-2 max-h-64 overflow-y-auto"> |
|
|
<div |
|
|
<div |
|
@ -148,7 +170,10 @@ |
|
|
<div class="flex items-center gap-2"> |
|
|
<div class="flex items-center gap-2"> |
|
|
<font-awesome icon="folder" class="fa-fw text-yellow-500" /> |
|
|
<font-awesome icon="folder" class="fa-fw text-yellow-500" /> |
|
|
<span class="font-medium">{{ entry.name }}</span> |
|
|
<span class="font-medium">{{ entry.name }}</span> |
|
|
<span class="text-xs bg-gray-200 text-gray-700 px-2 py-0.5 rounded-full ml-2">Folder</span> |
|
|
<span |
|
|
|
|
|
class="text-xs bg-gray-200 text-gray-700 px-2 py-0.5 rounded-full ml-2" |
|
|
|
|
|
>Folder</span |
|
|
|
|
|
> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div |
|
|
<div |
|
@ -164,14 +189,18 @@ |
|
|
<div class="text-sm text-gray-500 mt-1"> |
|
|
<div class="text-sm text-gray-500 mt-1"> |
|
|
<span v-if="entry.size">{{ formatFileSize(entry.size) }}</span> |
|
|
<span v-if="entry.size">{{ formatFileSize(entry.size) }}</span> |
|
|
<span v-else>Size unknown</span> |
|
|
<span v-else>Size unknown</span> |
|
|
<span v-if="entry.path && !platformCapabilities.isIOS" class="ml-2 text-xs text-blue-600">📁 {{ entry.path }}</span> |
|
|
<span |
|
|
|
|
|
v-if="entry.path && !platformCapabilities.isIOS" |
|
|
|
|
|
class="ml-2 text-xs text-blue-600" |
|
|
|
|
|
>📁 {{ entry.path }}</span |
|
|
|
|
|
> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex gap-2 ml-3"> |
|
|
<div class="flex gap-2 ml-3"> |
|
|
<button |
|
|
<button |
|
|
@click="openFile(entry.uri, entry.name)" |
|
|
|
|
|
class="text-blue-500 hover:text-blue-700 p-1" |
|
|
class="text-blue-500 hover:text-blue-700 p-1" |
|
|
title="Open file" |
|
|
title="Open file" |
|
|
|
|
|
@click="openFile(entry.uri, entry.name)" |
|
|
> |
|
|
> |
|
|
<font-awesome icon="external-link-alt" class="fa-fw" /> |
|
|
<font-awesome icon="external-link-alt" class="fa-fw" /> |
|
|
</button> |
|
|
</button> |
|
@ -181,15 +210,28 @@ |
|
|
|
|
|
|
|
|
<!-- Summary --> |
|
|
<!-- Summary --> |
|
|
<div class="text-sm text-gray-500 mt-3 pt-3 border-t"> |
|
|
<div class="text-sm text-gray-500 mt-3 pt-3 border-t"> |
|
|
Showing {{ filteredFiles.length }} of {{ backupFiles.length }} backup files |
|
|
Showing {{ filteredFiles.length }} of {{ backupFiles.length }} backup |
|
|
|
|
|
files |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="text-sm text-gray-600 mb-2"> |
|
|
<div class="text-sm text-gray-600 mb-2"> |
|
|
<p>📁 Backup files are saved to persistent storage that survives app installations:</p> |
|
|
<p> |
|
|
|
|
|
📁 Backup files are saved to persistent storage that survives app |
|
|
|
|
|
installations: |
|
|
|
|
|
</p> |
|
|
<ul class="list-disc list-inside ml-2 mt-1 text-xs"> |
|
|
<ul class="list-disc list-inside ml-2 mt-1 text-xs"> |
|
|
<li v-if="platformCapabilities.isIOS">iOS: Documents folder (accessible via Files app)</li> |
|
|
<li v-if="platformCapabilities.isIOS"> |
|
|
<li v-if="platformCapabilities.isMobile && !platformCapabilities.isIOS">Android: Downloads/TimeSafari or external storage (accessible via file managers)</li> |
|
|
iOS: Documents folder (accessible via Files app) |
|
|
<li v-if="!platformCapabilities.isMobile">Desktop: User's download directory</li> |
|
|
</li> |
|
|
|
|
|
<li |
|
|
|
|
|
v-if="platformCapabilities.isMobile && !platformCapabilities.isIOS" |
|
|
|
|
|
> |
|
|
|
|
|
Android: Downloads/TimeSafari or external storage (accessible via |
|
|
|
|
|
file managers) |
|
|
|
|
|
</li> |
|
|
|
|
|
<li v-if="!platformCapabilities.isMobile"> |
|
|
|
|
|
Desktop: User's download directory |
|
|
|
|
|
</li> |
|
|
</ul> |
|
|
</ul> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
@ -222,7 +264,8 @@ export default class BackupFilesList extends Vue { |
|
|
/** |
|
|
/** |
|
|
* Platform service instance for platform-specific operations |
|
|
* Platform service instance for platform-specific operations |
|
|
*/ |
|
|
*/ |
|
|
private platformService: PlatformService = PlatformServiceFactory.getInstance(); |
|
|
private platformService: PlatformService = |
|
|
|
|
|
PlatformServiceFactory.getInstance(); |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Platform capabilities for the current platform |
|
|
* Platform capabilities for the current platform |
|
@ -234,12 +277,18 @@ export default class BackupFilesList extends Vue { |
|
|
/** |
|
|
/** |
|
|
* List of backup files found on the device |
|
|
* List of backup files found on the device |
|
|
*/ |
|
|
*/ |
|
|
backupFiles: Array<{name: string, uri: string, size?: number, type: 'contacts' | 'seed' | 'other', path?: string}> = []; |
|
|
backupFiles: Array<{ |
|
|
|
|
|
name: string; |
|
|
|
|
|
uri: string; |
|
|
|
|
|
size?: number; |
|
|
|
|
|
type: "contacts" | "seed" | "other"; |
|
|
|
|
|
path?: string; |
|
|
|
|
|
}> = []; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Currently selected file type filter |
|
|
* Currently selected file type filter |
|
|
*/ |
|
|
*/ |
|
|
selectedType: 'all' | 'contacts' | 'seed' | 'other' = 'all'; |
|
|
selectedType: "all" | "contacts" | "seed" | "other" = "all"; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Loading state for file operations |
|
|
* Loading state for file operations |
|
@ -259,7 +308,13 @@ export default class BackupFilesList extends Vue { |
|
|
/** |
|
|
/** |
|
|
* List of files/folders in the current directory |
|
|
* List of files/folders in the current directory |
|
|
*/ |
|
|
*/ |
|
|
directoryEntries: Array<{name: string, uri: string, size?: number, path: string, type: 'file' | 'folder'}> = []; |
|
|
directoryEntries: Array<{ |
|
|
|
|
|
name: string; |
|
|
|
|
|
uri: string; |
|
|
|
|
|
size?: number; |
|
|
|
|
|
path: string; |
|
|
|
|
|
type: "file" | "folder"; |
|
|
|
|
|
}> = []; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Temporary debug mode to show all entries as files |
|
|
* Temporary debug mode to show all entries as files |
|
@ -271,28 +326,37 @@ export default class BackupFilesList extends Vue { |
|
|
* Returns true if permission is granted, false otherwise. |
|
|
* Returns true if permission is granted, false otherwise. |
|
|
*/ |
|
|
*/ |
|
|
private async ensureStoragePermission(): Promise<boolean> { |
|
|
private async ensureStoragePermission(): Promise<boolean> { |
|
|
logger.log('[BackupFilesList] ensureStoragePermission called. platformCapabilities:', this.platformCapabilities); |
|
|
logger.log( |
|
|
|
|
|
"[BackupFilesList] ensureStoragePermission called. platformCapabilities:", |
|
|
|
|
|
this.platformCapabilities, |
|
|
|
|
|
); |
|
|
if (!this.platformCapabilities.hasFileSystem) return true; |
|
|
if (!this.platformCapabilities.hasFileSystem) return true; |
|
|
// Only relevant for native platforms (Android/iOS) |
|
|
// Only relevant for native platforms (Android/iOS) |
|
|
const platformService = this.platformService as any; |
|
|
const platformService = this.platformService as any; |
|
|
if (typeof platformService.checkStoragePermissions === 'function') { |
|
|
if (typeof platformService.checkStoragePermissions === "function") { |
|
|
try { |
|
|
try { |
|
|
await platformService.checkStoragePermissions(); |
|
|
await platformService.checkStoragePermissions(); |
|
|
logger.log('[BackupFilesList] Storage permission granted.'); |
|
|
logger.log("[BackupFilesList] Storage permission granted."); |
|
|
return true; |
|
|
return true; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.error('[BackupFilesList] Storage permission denied:', error); |
|
|
logger.error("[BackupFilesList] Storage permission denied:", error); |
|
|
|
|
|
|
|
|
// Get specific guidance for the platform |
|
|
// Get specific guidance for the platform |
|
|
let guidance = "This app needs permission to access your files to list and restore backups."; |
|
|
let guidance = |
|
|
if (typeof platformService.getStoragePermissionGuidance === 'function') { |
|
|
"This app needs permission to access your files to list and restore backups."; |
|
|
|
|
|
if ( |
|
|
|
|
|
typeof platformService.getStoragePermissionGuidance === "function" |
|
|
|
|
|
) { |
|
|
try { |
|
|
try { |
|
|
guidance = await platformService.getStoragePermissionGuidance(); |
|
|
guidance = await platformService.getStoragePermissionGuidance(); |
|
|
} catch (guidanceError) { |
|
|
} catch (guidanceError) { |
|
|
logger.warn('[BackupFilesList] Could not get permission guidance:', guidanceError); |
|
|
logger.warn( |
|
|
|
|
|
"[BackupFilesList] Could not get permission guidance:", |
|
|
|
|
|
guidanceError, |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.$notify( |
|
|
this.$notify( |
|
|
{ |
|
|
{ |
|
|
group: "alert", |
|
|
group: "alert", |
|
@ -312,21 +376,27 @@ export default class BackupFilesList extends Vue { |
|
|
* Lifecycle hook to load backup files when component is mounted |
|
|
* Lifecycle hook to load backup files when component is mounted |
|
|
*/ |
|
|
*/ |
|
|
async mounted() { |
|
|
async mounted() { |
|
|
logger.log('[BackupFilesList] mounted hook called. platformCapabilities:', this.platformCapabilities); |
|
|
logger.log( |
|
|
|
|
|
"[BackupFilesList] mounted hook called. platformCapabilities:", |
|
|
|
|
|
this.platformCapabilities, |
|
|
|
|
|
); |
|
|
if (this.platformCapabilities.hasFileSystem) { |
|
|
if (this.platformCapabilities.hasFileSystem) { |
|
|
// Check/request permission before loading |
|
|
// Check/request permission before loading |
|
|
const hasPermission = await this.ensureStoragePermission(); |
|
|
const hasPermission = await this.ensureStoragePermission(); |
|
|
if (hasPermission) { |
|
|
if (hasPermission) { |
|
|
// Set default root path |
|
|
// Set default root path |
|
|
if (this.platformCapabilities.isIOS) { |
|
|
if (this.platformCapabilities.isIOS) { |
|
|
this.currentPath = ['.']; |
|
|
this.currentPath = ["."]; |
|
|
} else { |
|
|
} else { |
|
|
this.currentPath = ['Download', 'TimeSafari']; |
|
|
this.currentPath = ["Download", "TimeSafari"]; |
|
|
} |
|
|
} |
|
|
await this.loadDirectory(); |
|
|
await this.loadDirectory(); |
|
|
this.refreshInterval = window.setInterval(() => { |
|
|
this.refreshInterval = window.setInterval( |
|
|
this.loadDirectory(); |
|
|
() => { |
|
|
}, 5 * 60 * 1000); |
|
|
this.loadDirectory(); |
|
|
|
|
|
}, |
|
|
|
|
|
5 * 60 * 1000, |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -346,12 +416,17 @@ export default class BackupFilesList extends Vue { |
|
|
* Note: The 'All' tab count is sometimes too small. Logging for debugging. |
|
|
* Note: The 'All' tab count is sometimes too small. Logging for debugging. |
|
|
*/ |
|
|
*/ |
|
|
get filteredFiles() { |
|
|
get filteredFiles() { |
|
|
if (this.selectedType === 'all') { |
|
|
if (this.selectedType === "all") { |
|
|
logger.log('[BackupFilesList] filteredFiles (All):', this.backupFiles); |
|
|
logger.log("[BackupFilesList] filteredFiles (All):", this.backupFiles); |
|
|
return this.backupFiles; |
|
|
return this.backupFiles; |
|
|
} |
|
|
} |
|
|
const filtered = this.backupFiles.filter(file => file.type === this.selectedType); |
|
|
const filtered = this.backupFiles.filter( |
|
|
logger.log(`[BackupFilesList] filteredFiles (${this.selectedType}):`, filtered); |
|
|
(file) => file.type === this.selectedType, |
|
|
|
|
|
); |
|
|
|
|
|
logger.log( |
|
|
|
|
|
`[BackupFilesList] filteredFiles (${this.selectedType}):`, |
|
|
|
|
|
filtered, |
|
|
|
|
|
); |
|
|
return filtered; |
|
|
return filtered; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -369,11 +444,18 @@ export default class BackupFilesList extends Vue { |
|
|
if (!this.platformCapabilities.hasFileSystem) return; |
|
|
if (!this.platformCapabilities.hasFileSystem) return; |
|
|
this.isLoading = true; |
|
|
this.isLoading = true; |
|
|
try { |
|
|
try { |
|
|
const path = this.currentPath.join('/') || (this.platformCapabilities.isIOS ? '.' : 'Download/TimeSafari'); |
|
|
const path = |
|
|
this.directoryEntries = await (this.platformService as any).listFilesInDirectory(path, this.debugShowAll); |
|
|
this.currentPath.join("/") || |
|
|
logger.log('[BackupFilesList] Loaded directory:', { path, entries: this.directoryEntries }); |
|
|
(this.platformCapabilities.isIOS ? "." : "Download/TimeSafari"); |
|
|
|
|
|
this.directoryEntries = await ( |
|
|
|
|
|
this.platformService as PlatformService |
|
|
|
|
|
).listFilesInDirectory(path, this.debugShowAll); |
|
|
|
|
|
logger.log("[BackupFilesList] Loaded directory:", { |
|
|
|
|
|
path, |
|
|
|
|
|
entries: this.directoryEntries, |
|
|
|
|
|
}); |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.error('[BackupFilesList] Failed to load directory:', error); |
|
|
logger.error("[BackupFilesList] Failed to load directory:", error); |
|
|
this.directoryEntries = []; |
|
|
this.directoryEntries = []; |
|
|
} finally { |
|
|
} finally { |
|
|
this.isLoading = false; |
|
|
this.isLoading = false; |
|
@ -417,17 +499,17 @@ export default class BackupFilesList extends Vue { |
|
|
* Computed property for showing files and folders |
|
|
* Computed property for showing files and folders |
|
|
*/ |
|
|
*/ |
|
|
get folders() { |
|
|
get folders() { |
|
|
return this.directoryEntries.filter(e => e.type === 'folder'); |
|
|
return this.directoryEntries.filter((e) => e.type === "folder"); |
|
|
} |
|
|
} |
|
|
get files() { |
|
|
get files() { |
|
|
return this.directoryEntries.filter(e => e.type === 'file'); |
|
|
return this.directoryEntries.filter((e) => e.type === "file"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Refreshes the list of backup files from the device |
|
|
* Refreshes the list of backup files from the device |
|
|
*/ |
|
|
*/ |
|
|
async refreshFiles() { |
|
|
async refreshFiles() { |
|
|
logger.log('[BackupFilesList] refreshFiles called.'); |
|
|
logger.log("[BackupFilesList] refreshFiles called."); |
|
|
if (!this.platformCapabilities.hasFileSystem) { |
|
|
if (!this.platformCapabilities.hasFileSystem) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
@ -441,29 +523,32 @@ export default class BackupFilesList extends Vue { |
|
|
this.isLoading = true; |
|
|
this.isLoading = true; |
|
|
try { |
|
|
try { |
|
|
this.backupFiles = await this.platformService.listBackupFiles(); |
|
|
this.backupFiles = await this.platformService.listBackupFiles(); |
|
|
logger.log('[BackupFilesList] Refreshed backup files:', { |
|
|
logger.log("[BackupFilesList] Refreshed backup files:", { |
|
|
count: this.backupFiles.length, |
|
|
count: this.backupFiles.length, |
|
|
files: this.backupFiles.map(f => ({ |
|
|
files: this.backupFiles.map((f) => ({ |
|
|
name: f.name, |
|
|
name: f.name, |
|
|
type: f.type, |
|
|
type: f.type, |
|
|
path: f.path, |
|
|
path: f.path, |
|
|
size: f.size |
|
|
size: f.size, |
|
|
})), |
|
|
})), |
|
|
platform: this.platformCapabilities.isIOS ? "iOS" : "Android", |
|
|
platform: this.platformCapabilities.isIOS ? "iOS" : "Android", |
|
|
timestamp: new Date().toISOString(), |
|
|
timestamp: new Date().toISOString(), |
|
|
}); |
|
|
}); |
|
|
// Debug: Log file type distribution |
|
|
// Debug: Log file type distribution |
|
|
const typeCounts = { |
|
|
const typeCounts = { |
|
|
contacts: this.backupFiles.filter(f => f.type === 'contacts').length, |
|
|
contacts: this.backupFiles.filter((f) => f.type === "contacts").length, |
|
|
seed: this.backupFiles.filter(f => f.type === 'seed').length, |
|
|
seed: this.backupFiles.filter((f) => f.type === "seed").length, |
|
|
other: this.backupFiles.filter(f => f.type === 'other').length, |
|
|
other: this.backupFiles.filter((f) => f.type === "other").length, |
|
|
total: this.backupFiles.length |
|
|
total: this.backupFiles.length, |
|
|
}; |
|
|
}; |
|
|
logger.log('[BackupFilesList] File type distribution:', typeCounts); |
|
|
logger.log("[BackupFilesList] File type distribution:", typeCounts); |
|
|
// Log the full backupFiles array for debugging the 'All' tab count |
|
|
// Log the full backupFiles array for debugging the 'All' tab count |
|
|
logger.log('[BackupFilesList] backupFiles array for All tab:', this.backupFiles); |
|
|
logger.log( |
|
|
|
|
|
"[BackupFilesList] backupFiles array for All tab:", |
|
|
|
|
|
this.backupFiles, |
|
|
|
|
|
); |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.error('[BackupFilesList] Failed to refresh backup files:', error); |
|
|
logger.error("[BackupFilesList] Failed to refresh backup files:", error); |
|
|
this.$notify( |
|
|
this.$notify( |
|
|
{ |
|
|
{ |
|
|
group: "alert", |
|
|
group: "alert", |
|
@ -484,17 +569,17 @@ export default class BackupFilesList extends Vue { |
|
|
async createTestBackup() { |
|
|
async createTestBackup() { |
|
|
try { |
|
|
try { |
|
|
this.isLoading = true; |
|
|
this.isLoading = true; |
|
|
logger.log('[BackupFilesList] Creating test backup file'); |
|
|
logger.log("[BackupFilesList] Creating test backup file"); |
|
|
|
|
|
|
|
|
const result = await this.platformService.createTestBackupFile(); |
|
|
const result = await this.platformService.createTestBackupFile(); |
|
|
|
|
|
|
|
|
if (result.success) { |
|
|
if (result.success) { |
|
|
logger.log('[BackupFilesList] Test backup file created successfully:', { |
|
|
logger.log("[BackupFilesList] Test backup file created successfully:", { |
|
|
fileName: result.fileName, |
|
|
fileName: result.fileName, |
|
|
uri: result.uri, |
|
|
uri: result.uri, |
|
|
timestamp: new Date().toISOString(), |
|
|
timestamp: new Date().toISOString(), |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
this.$notify( |
|
|
this.$notify( |
|
|
{ |
|
|
{ |
|
|
group: "alert", |
|
|
group: "alert", |
|
@ -504,14 +589,17 @@ export default class BackupFilesList extends Vue { |
|
|
}, |
|
|
}, |
|
|
5000, |
|
|
5000, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
// Refresh the file list to show the new test file |
|
|
// Refresh the file list to show the new test file |
|
|
await this.refreshFiles(); |
|
|
await this.refreshFiles(); |
|
|
} else { |
|
|
} else { |
|
|
throw new Error(result.error || "Failed to create test backup file"); |
|
|
throw new Error(result.error || "Failed to create test backup file"); |
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.error('[BackupFilesList] Failed to create test backup file:', error); |
|
|
logger.error( |
|
|
|
|
|
"[BackupFilesList] Failed to create test backup file:", |
|
|
|
|
|
error, |
|
|
|
|
|
); |
|
|
this.$notify( |
|
|
this.$notify( |
|
|
{ |
|
|
{ |
|
|
group: "alert", |
|
|
group: "alert", |
|
@ -532,12 +620,15 @@ export default class BackupFilesList extends Vue { |
|
|
async testDirectoryContexts() { |
|
|
async testDirectoryContexts() { |
|
|
try { |
|
|
try { |
|
|
this.isLoading = true; |
|
|
this.isLoading = true; |
|
|
logger.log('[BackupFilesList] Testing directory contexts'); |
|
|
logger.log("[BackupFilesList] Testing directory contexts"); |
|
|
|
|
|
|
|
|
const debugOutput = await this.platformService.testDirectoryContexts(); |
|
|
const debugOutput = await this.platformService.testDirectoryContexts(); |
|
|
|
|
|
|
|
|
logger.log('[BackupFilesList] Directory context test results:', debugOutput); |
|
|
logger.log( |
|
|
|
|
|
"[BackupFilesList] Directory context test results:", |
|
|
|
|
|
debugOutput, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
// Show the debug output in a notification or alert |
|
|
// Show the debug output in a notification or alert |
|
|
this.$notify( |
|
|
this.$notify( |
|
|
{ |
|
|
{ |
|
@ -548,14 +639,16 @@ export default class BackupFilesList extends Vue { |
|
|
}, |
|
|
}, |
|
|
5000, |
|
|
5000, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
// Also log the full output to console for easy access |
|
|
// Also log the full output to console for easy access |
|
|
console.log("=== Directory Context Test Results ==="); |
|
|
logger.log("=== Directory Context Test Results ==="); |
|
|
console.log(debugOutput); |
|
|
logger.log(debugOutput); |
|
|
console.log("=== End Test Results ==="); |
|
|
logger.log("=== End Test Results ==="); |
|
|
|
|
|
|
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.error('[BackupFilesList] Failed to test directory contexts:', error); |
|
|
logger.error( |
|
|
|
|
|
"[BackupFilesList] Failed to test directory contexts:", |
|
|
|
|
|
error, |
|
|
|
|
|
); |
|
|
this.$notify( |
|
|
this.$notify( |
|
|
{ |
|
|
{ |
|
|
group: "alert", |
|
|
group: "alert", |
|
@ -575,7 +668,7 @@ export default class BackupFilesList extends Vue { |
|
|
* This method can be called from parent components |
|
|
* This method can be called from parent components |
|
|
*/ |
|
|
*/ |
|
|
async refreshAfterSave() { |
|
|
async refreshAfterSave() { |
|
|
logger.log('[BackupFilesList] refreshAfterSave called'); |
|
|
logger.log("[BackupFilesList] refreshAfterSave called"); |
|
|
await this.refreshFiles(); |
|
|
await this.refreshFiles(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -587,7 +680,7 @@ export default class BackupFilesList extends Vue { |
|
|
async openFile(fileUri: string, fileName: string) { |
|
|
async openFile(fileUri: string, fileName: string) { |
|
|
try { |
|
|
try { |
|
|
const result = await this.platformService.openFile(fileUri, fileName); |
|
|
const result = await this.platformService.openFile(fileUri, fileName); |
|
|
|
|
|
|
|
|
if (result.success) { |
|
|
if (result.success) { |
|
|
logger.log("[BackupFilesList] File opened successfully:", { |
|
|
logger.log("[BackupFilesList] File opened successfully:", { |
|
|
fileName, |
|
|
fileName, |
|
@ -616,7 +709,7 @@ export default class BackupFilesList extends Vue { |
|
|
async openBackupDirectory() { |
|
|
async openBackupDirectory() { |
|
|
try { |
|
|
try { |
|
|
const result = await this.platformService.openBackupDirectory(); |
|
|
const result = await this.platformService.openBackupDirectory(); |
|
|
|
|
|
|
|
|
if (result.success) { |
|
|
if (result.success) { |
|
|
logger.log("[BackupFilesList] Backup directory opened successfully:", { |
|
|
logger.log("[BackupFilesList] Backup directory opened successfully:", { |
|
|
timestamp: new Date().toISOString(), |
|
|
timestamp: new Date().toISOString(), |
|
@ -642,14 +735,18 @@ export default class BackupFilesList extends Vue { |
|
|
* Gets the count of files for a specific type |
|
|
* Gets the count of files for a specific type |
|
|
* Note: The 'All' tab count is sometimes too small. Logging for debugging. |
|
|
* Note: The 'All' tab count is sometimes too small. Logging for debugging. |
|
|
*/ |
|
|
*/ |
|
|
getFileCountByType(type: 'all' | 'contacts' | 'seed' | 'other'): number { |
|
|
getFileCountByType(type: "all" | "contacts" | "seed" | "other"): number { |
|
|
let count; |
|
|
let count; |
|
|
if (type === 'all') { |
|
|
if (type === "all") { |
|
|
count = this.backupFiles.length; |
|
|
count = this.backupFiles.length; |
|
|
logger.log('[BackupFilesList] getFileCountByType (All):', count, this.backupFiles); |
|
|
logger.log( |
|
|
|
|
|
"[BackupFilesList] getFileCountByType (All):", |
|
|
|
|
|
count, |
|
|
|
|
|
this.backupFiles, |
|
|
|
|
|
); |
|
|
return count; |
|
|
return count; |
|
|
} |
|
|
} |
|
|
count = this.backupFiles.filter(file => file.type === type).length; |
|
|
count = this.backupFiles.filter((file) => file.type === type).length; |
|
|
logger.log(`[BackupFilesList] getFileCountByType (${type}):`, count); |
|
|
logger.log(`[BackupFilesList] getFileCountByType (${type}):`, count); |
|
|
return count; |
|
|
return count; |
|
|
} |
|
|
} |
|
@ -659,14 +756,14 @@ export default class BackupFilesList extends Vue { |
|
|
* @param type - File type |
|
|
* @param type - File type |
|
|
* @returns FontAwesome icon name |
|
|
* @returns FontAwesome icon name |
|
|
*/ |
|
|
*/ |
|
|
getFileIcon(type: 'contacts' | 'seed' | 'other'): string { |
|
|
getFileIcon(type: "contacts" | "seed" | "other"): string { |
|
|
switch (type) { |
|
|
switch (type) { |
|
|
case 'contacts': |
|
|
case "contacts": |
|
|
return 'address-book'; |
|
|
return "address-book"; |
|
|
case 'seed': |
|
|
case "seed": |
|
|
return 'key'; |
|
|
return "key"; |
|
|
default: |
|
|
default: |
|
|
return 'file-alt'; |
|
|
return "file-alt"; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -675,14 +772,14 @@ export default class BackupFilesList extends Vue { |
|
|
* @param type - File type |
|
|
* @param type - File type |
|
|
* @returns CSS color class |
|
|
* @returns CSS color class |
|
|
*/ |
|
|
*/ |
|
|
getFileIconColor(type: 'contacts' | 'seed' | 'other'): string { |
|
|
getFileIconColor(type: "contacts" | "seed" | "other"): string { |
|
|
switch (type) { |
|
|
switch (type) { |
|
|
case 'contacts': |
|
|
case "contacts": |
|
|
return 'text-blue-500'; |
|
|
return "text-blue-500"; |
|
|
case 'seed': |
|
|
case "seed": |
|
|
return 'text-orange-500'; |
|
|
return "text-orange-500"; |
|
|
default: |
|
|
default: |
|
|
return 'text-gray-500'; |
|
|
return "text-gray-500"; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -691,14 +788,14 @@ export default class BackupFilesList extends Vue { |
|
|
* @param type - File type |
|
|
* @param type - File type |
|
|
* @returns CSS color class |
|
|
* @returns CSS color class |
|
|
*/ |
|
|
*/ |
|
|
getTypeBadgeColor(type: 'contacts' | 'seed' | 'other'): string { |
|
|
getTypeBadgeColor(type: "contacts" | "seed" | "other"): string { |
|
|
switch (type) { |
|
|
switch (type) { |
|
|
case 'contacts': |
|
|
case "contacts": |
|
|
return 'bg-blue-100 text-blue-800'; |
|
|
return "bg-blue-100 text-blue-800"; |
|
|
case 'seed': |
|
|
case "seed": |
|
|
return 'bg-orange-100 text-orange-800'; |
|
|
return "bg-orange-100 text-orange-800"; |
|
|
default: |
|
|
default: |
|
|
return 'bg-gray-100 text-gray-800'; |
|
|
return "bg-gray-100 text-gray-800"; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -708,13 +805,13 @@ export default class BackupFilesList extends Vue { |
|
|
* @returns Formatted file size string |
|
|
* @returns Formatted file size string |
|
|
*/ |
|
|
*/ |
|
|
formatFileSize(bytes: number): string { |
|
|
formatFileSize(bytes: number): string { |
|
|
if (bytes === 0) return '0 Bytes'; |
|
|
if (bytes === 0) return "0 Bytes"; |
|
|
|
|
|
|
|
|
const k = 1024; |
|
|
const k = 1024; |
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB']; |
|
|
const sizes = ["Bytes", "KB", "MB", "GB"]; |
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k)); |
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k)); |
|
|
|
|
|
|
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; |
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@ -724,44 +821,62 @@ export default class BackupFilesList extends Vue { |
|
|
public async debugFileDiscovery() { |
|
|
public async debugFileDiscovery() { |
|
|
try { |
|
|
try { |
|
|
logger.log("[BackupFilesList] Starting debug file discovery..."); |
|
|
logger.log("[BackupFilesList] Starting debug file discovery..."); |
|
|
|
|
|
|
|
|
// Test the platform service's test methods |
|
|
// Test the platform service's test methods |
|
|
const platformService = PlatformServiceFactory.getInstance(); |
|
|
const platformService = PlatformServiceFactory.getInstance(); |
|
|
|
|
|
|
|
|
// Test listing all user files |
|
|
// Test listing all user files |
|
|
const allFilesResult = await platformService.testListUserFiles(); |
|
|
const allFilesResult = await platformService.testListUserFiles(); |
|
|
logger.log("[BackupFilesList] All user files test result:", allFilesResult); |
|
|
logger.log( |
|
|
|
|
|
"[BackupFilesList] All user files test result:", |
|
|
|
|
|
allFilesResult, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
// Test listing backup files specifically |
|
|
// Test listing backup files specifically |
|
|
const backupFilesResult = await platformService.testBackupFiles(); |
|
|
const backupFilesResult = await platformService.testBackupFiles(); |
|
|
logger.log("[BackupFilesList] Backup files test result:", backupFilesResult); |
|
|
logger.log( |
|
|
|
|
|
"[BackupFilesList] Backup files test result:", |
|
|
// Test listing all backup files (if available) |
|
|
backupFilesResult, |
|
|
if ('testListAllBackupFiles' in platformService) { |
|
|
); |
|
|
const allBackupFilesResult = await (platformService as any).testListAllBackupFiles(); |
|
|
|
|
|
logger.log("[BackupFilesList] All backup files test result:", allBackupFilesResult); |
|
|
// Note: testListAllBackupFiles method is not part of the PlatformService interface |
|
|
} |
|
|
// It exists only in CapacitorPlatformService implementation |
|
|
|
|
|
// If needed, this could be added to the interface or called via type assertion |
|
|
|
|
|
|
|
|
// Test debug listing all files without filtering (if available) |
|
|
// Test debug listing all files without filtering (if available) |
|
|
if ('debugListAllFiles' in platformService) { |
|
|
if ("debugListAllFiles" in platformService) { |
|
|
const debugAllFiles = await (platformService as any).debugListAllFiles(); |
|
|
const debugAllFiles = await ( |
|
|
|
|
|
platformService as any |
|
|
|
|
|
).debugListAllFiles(); |
|
|
logger.log("[BackupFilesList] Debug all files (no filtering):", { |
|
|
logger.log("[BackupFilesList] Debug all files (no filtering):", { |
|
|
count: debugAllFiles.length, |
|
|
count: debugAllFiles.length, |
|
|
files: debugAllFiles.map((f: any) => ({ name: f.name, path: f.path, size: f.size })) |
|
|
files: debugAllFiles.map((f: any) => ({ |
|
|
|
|
|
name: f.name, |
|
|
|
|
|
path: f.path, |
|
|
|
|
|
size: f.size, |
|
|
|
|
|
})), |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Test comprehensive step-by-step debug (if available) |
|
|
// Test comprehensive step-by-step debug (if available) |
|
|
if ('debugFileDiscoveryStepByStep' in platformService) { |
|
|
if ("debugFileDiscoveryStepByStep" in platformService) { |
|
|
const stepByStepDebug = await (platformService as any).debugFileDiscoveryStepByStep(); |
|
|
const stepByStepDebug = await ( |
|
|
logger.log("[BackupFilesList] Step-by-step debug output:", stepByStepDebug); |
|
|
platformService as any |
|
|
|
|
|
).debugFileDiscoveryStepByStep(); |
|
|
|
|
|
logger.log( |
|
|
|
|
|
"[BackupFilesList] Step-by-step debug output:", |
|
|
|
|
|
stepByStepDebug, |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
return { |
|
|
allFiles: allFilesResult, |
|
|
allFiles: allFilesResult, |
|
|
backupFiles: backupFilesResult, |
|
|
backupFiles: backupFilesResult, |
|
|
currentBackupFiles: this.backupFiles, |
|
|
currentBackupFiles: this.backupFiles, |
|
|
debugAllFiles: 'debugListAllFiles' in platformService ? await (platformService as any).debugListAllFiles() : null |
|
|
debugAllFiles: |
|
|
|
|
|
"debugListAllFiles" in platformService |
|
|
|
|
|
? await (platformService as any).debugListAllFiles() |
|
|
|
|
|
: null, |
|
|
}; |
|
|
}; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.error("[BackupFilesList] Debug file discovery failed:", error); |
|
|
logger.error("[BackupFilesList] Debug file discovery failed:", error); |
|
@ -769,11 +884,11 @@ export default class BackupFilesList extends Vue { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Watch('platformCapabilities.hasFileSystem', { immediate: true }) |
|
|
@Watch("platformCapabilities.hasFileSystem", { immediate: true }) |
|
|
async onFileSystemCapabilityChanged(newVal: boolean) { |
|
|
async onFileSystemCapabilityChanged(newVal: boolean) { |
|
|
if (newVal) { |
|
|
if (newVal) { |
|
|
await this.refreshFiles(); |
|
|
await this.refreshFiles(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
</script> |
|
|
</script> |
|
|