chore: linting; more non-errors need fixing
This commit is contained in:
@@ -1,18 +1,10 @@
|
||||
/**
|
||||
* Backup Files List Component
|
||||
*
|
||||
* Displays a list of backup files saved by 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 backup directory in the device's file explorer
|
||||
*
|
||||
* @component
|
||||
* @displayName BackupFilesList
|
||||
* @example
|
||||
* ```vue
|
||||
* <BackupFilesList />
|
||||
* ```
|
||||
*/
|
||||
/** * Backup Files List Component * * Displays a list of backup files saved by
|
||||
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
|
||||
backup directory in the device's file explorer * * @component * @displayName
|
||||
BackupFilesList * @example * ```vue *
|
||||
<BackupFilesList />
|
||||
* ``` */
|
||||
|
||||
<template>
|
||||
<div class="backup-files-list">
|
||||
@@ -21,9 +13,9 @@
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
v-if="platformCapabilities.hasFileSystem"
|
||||
@click="refreshFiles()"
|
||||
class="text-sm bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded"
|
||||
:disabled="isLoading"
|
||||
@click="refreshFiles()"
|
||||
>
|
||||
<font-awesome
|
||||
icon="refresh"
|
||||
@@ -34,34 +26,34 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="platformCapabilities.hasFileSystem"
|
||||
@click="openBackupDirectory()"
|
||||
class="text-sm bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded"
|
||||
:disabled="isLoading"
|
||||
@click="openBackupDirectory()"
|
||||
>
|
||||
<font-awesome icon="folder-open" class="fa-fw" />
|
||||
Open Directory
|
||||
</button>
|
||||
<button
|
||||
v-if="platformCapabilities.hasFileSystem && isDevelopment"
|
||||
@click="debugFileDiscovery()"
|
||||
class="text-sm bg-yellow-500 hover:bg-yellow-600 text-white px-3 py-1 rounded"
|
||||
:disabled="isLoading"
|
||||
title="Debug file discovery (development only)"
|
||||
@click="debugFileDiscovery()"
|
||||
>
|
||||
<font-awesome icon="bug" class="fa-fw" />
|
||||
Debug
|
||||
</button>
|
||||
<button
|
||||
@click="createTestBackup"
|
||||
:disabled="isLoading"
|
||||
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
|
||||
</button>
|
||||
<button
|
||||
@click="testDirectoryContexts"
|
||||
:disabled="isLoading"
|
||||
class="px-3 py-1 bg-purple-500 text-white rounded text-sm hover:bg-purple-600 disabled:opacity-50"
|
||||
@click="testDirectoryContexts"
|
||||
>
|
||||
Test Contexts
|
||||
</button>
|
||||
@@ -73,21 +65,40 @@
|
||||
<p class="mt-2">Loading backup files...</p>
|
||||
</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" />
|
||||
<p>No backup files found</p>
|
||||
<p class="text-sm mt-1">Create backups using the export functions above</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>
|
||||
<p class="text-sm mt-1">
|
||||
Create backups using the export functions above
|
||||
</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">
|
||||
<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>• Backup files are saved to persistent storage that survives app 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>
|
||||
• Backup files are saved to persistent storage that survives app
|
||||
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 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>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -99,18 +110,20 @@
|
||||
<button
|
||||
v-for="type in ['all', 'contacts', 'seed', 'other'] as const"
|
||||
:key="type"
|
||||
@click="selectedType = type"
|
||||
:class="[
|
||||
'text-sm px-3 py-1 rounded border',
|
||||
selectedType === type
|
||||
? '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">
|
||||
({{ getFileCountByType(type) }})
|
||||
</span>
|
||||
{{
|
||||
type === "all"
|
||||
? "All"
|
||||
: type.charAt(0).toUpperCase() + type.slice(1)
|
||||
}}
|
||||
<span class="ml-1 text-xs"> ({{ getFileCountByType(type) }}) </span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -118,9 +131,9 @@
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span v-for="(crumb, idx) in breadcrumbs" :key="idx">
|
||||
<span
|
||||
v-if="idx < breadcrumbs.length - 1"
|
||||
class="text-blue-600 cursor-pointer underline"
|
||||
@click="goToBreadcrumb(idx)"
|
||||
v-if="idx < breadcrumbs.length - 1"
|
||||
>
|
||||
{{ crumb }}
|
||||
</span>
|
||||
@@ -129,14 +142,23 @@
|
||||
</span>
|
||||
</div>
|
||||
<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 class="mb-2">
|
||||
<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>
|
||||
</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 class="space-y-2 max-h-64 overflow-y-auto">
|
||||
<div
|
||||
@@ -148,7 +170,10 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<font-awesome icon="folder" class="fa-fw text-yellow-500" />
|
||||
<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
|
||||
@@ -164,14 +189,18 @@
|
||||
<div class="text-sm text-gray-500 mt-1">
|
||||
<span v-if="entry.size">{{ formatFileSize(entry.size) }}</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 class="flex gap-2 ml-3">
|
||||
<button
|
||||
@click="openFile(entry.uri, entry.name)"
|
||||
class="text-blue-500 hover:text-blue-700 p-1"
|
||||
title="Open file"
|
||||
@click="openFile(entry.uri, entry.name)"
|
||||
>
|
||||
<font-awesome icon="external-link-alt" class="fa-fw" />
|
||||
</button>
|
||||
@@ -181,15 +210,28 @@
|
||||
|
||||
<!-- Summary -->
|
||||
<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 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">
|
||||
<li v-if="platformCapabilities.isIOS">iOS: Documents folder (accessible via Files app)</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>
|
||||
<li v-if="platformCapabilities.isIOS">
|
||||
iOS: Documents folder (accessible via Files app)
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -222,7 +264,8 @@ export default class BackupFilesList extends Vue {
|
||||
/**
|
||||
* Platform service instance for platform-specific operations
|
||||
*/
|
||||
private platformService: PlatformService = PlatformServiceFactory.getInstance();
|
||||
private platformService: PlatformService =
|
||||
PlatformServiceFactory.getInstance();
|
||||
|
||||
/**
|
||||
* Platform capabilities for the current platform
|
||||
@@ -234,12 +277,18 @@ export default class BackupFilesList extends Vue {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
selectedType: 'all' | 'contacts' | 'seed' | 'other' = 'all';
|
||||
selectedType: "all" | "contacts" | "seed" | "other" = "all";
|
||||
|
||||
/**
|
||||
* Loading state for file operations
|
||||
@@ -259,7 +308,13 @@ export default class BackupFilesList extends Vue {
|
||||
/**
|
||||
* 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
|
||||
@@ -271,25 +326,34 @@ export default class BackupFilesList extends Vue {
|
||||
* Returns true if permission is granted, false otherwise.
|
||||
*/
|
||||
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;
|
||||
// Only relevant for native platforms (Android/iOS)
|
||||
const platformService = this.platformService as any;
|
||||
if (typeof platformService.checkStoragePermissions === 'function') {
|
||||
if (typeof platformService.checkStoragePermissions === "function") {
|
||||
try {
|
||||
await platformService.checkStoragePermissions();
|
||||
logger.log('[BackupFilesList] Storage permission granted.');
|
||||
logger.log("[BackupFilesList] Storage permission granted.");
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('[BackupFilesList] Storage permission denied:', error);
|
||||
logger.error("[BackupFilesList] Storage permission denied:", error);
|
||||
|
||||
// Get specific guidance for the platform
|
||||
let guidance = "This app needs permission to access your files to list and restore backups.";
|
||||
if (typeof platformService.getStoragePermissionGuidance === 'function') {
|
||||
let guidance =
|
||||
"This app needs permission to access your files to list and restore backups.";
|
||||
if (
|
||||
typeof platformService.getStoragePermissionGuidance === "function"
|
||||
) {
|
||||
try {
|
||||
guidance = await platformService.getStoragePermissionGuidance();
|
||||
} catch (guidanceError) {
|
||||
logger.warn('[BackupFilesList] Could not get permission guidance:', guidanceError);
|
||||
logger.warn(
|
||||
"[BackupFilesList] Could not get permission guidance:",
|
||||
guidanceError,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,21 +376,27 @@ export default class BackupFilesList extends Vue {
|
||||
* Lifecycle hook to load backup files when component is 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) {
|
||||
// Check/request permission before loading
|
||||
const hasPermission = await this.ensureStoragePermission();
|
||||
if (hasPermission) {
|
||||
// Set default root path
|
||||
if (this.platformCapabilities.isIOS) {
|
||||
this.currentPath = ['.'];
|
||||
this.currentPath = ["."];
|
||||
} else {
|
||||
this.currentPath = ['Download', 'TimeSafari'];
|
||||
this.currentPath = ["Download", "TimeSafari"];
|
||||
}
|
||||
await this.loadDirectory();
|
||||
this.refreshInterval = window.setInterval(() => {
|
||||
this.loadDirectory();
|
||||
}, 5 * 60 * 1000);
|
||||
this.refreshInterval = window.setInterval(
|
||||
() => {
|
||||
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.
|
||||
*/
|
||||
get filteredFiles() {
|
||||
if (this.selectedType === 'all') {
|
||||
logger.log('[BackupFilesList] filteredFiles (All):', this.backupFiles);
|
||||
if (this.selectedType === "all") {
|
||||
logger.log("[BackupFilesList] filteredFiles (All):", this.backupFiles);
|
||||
return this.backupFiles;
|
||||
}
|
||||
const filtered = this.backupFiles.filter(file => file.type === this.selectedType);
|
||||
logger.log(`[BackupFilesList] filteredFiles (${this.selectedType}):`, filtered);
|
||||
const filtered = this.backupFiles.filter(
|
||||
(file) => file.type === this.selectedType,
|
||||
);
|
||||
logger.log(
|
||||
`[BackupFilesList] filteredFiles (${this.selectedType}):`,
|
||||
filtered,
|
||||
);
|
||||
return filtered;
|
||||
}
|
||||
|
||||
@@ -369,11 +444,18 @@ export default class BackupFilesList extends Vue {
|
||||
if (!this.platformCapabilities.hasFileSystem) return;
|
||||
this.isLoading = true;
|
||||
try {
|
||||
const path = this.currentPath.join('/') || (this.platformCapabilities.isIOS ? '.' : 'Download/TimeSafari');
|
||||
this.directoryEntries = await (this.platformService as any).listFilesInDirectory(path, this.debugShowAll);
|
||||
logger.log('[BackupFilesList] Loaded directory:', { path, entries: this.directoryEntries });
|
||||
const path =
|
||||
this.currentPath.join("/") ||
|
||||
(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) {
|
||||
logger.error('[BackupFilesList] Failed to load directory:', error);
|
||||
logger.error("[BackupFilesList] Failed to load directory:", error);
|
||||
this.directoryEntries = [];
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
@@ -417,17 +499,17 @@ export default class BackupFilesList extends Vue {
|
||||
* Computed property for showing files and folders
|
||||
*/
|
||||
get folders() {
|
||||
return this.directoryEntries.filter(e => e.type === 'folder');
|
||||
return this.directoryEntries.filter((e) => e.type === "folder");
|
||||
}
|
||||
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
|
||||
*/
|
||||
async refreshFiles() {
|
||||
logger.log('[BackupFilesList] refreshFiles called.');
|
||||
logger.log("[BackupFilesList] refreshFiles called.");
|
||||
if (!this.platformCapabilities.hasFileSystem) {
|
||||
return;
|
||||
}
|
||||
@@ -441,29 +523,32 @@ export default class BackupFilesList extends Vue {
|
||||
this.isLoading = true;
|
||||
try {
|
||||
this.backupFiles = await this.platformService.listBackupFiles();
|
||||
logger.log('[BackupFilesList] Refreshed backup files:', {
|
||||
logger.log("[BackupFilesList] Refreshed backup files:", {
|
||||
count: this.backupFiles.length,
|
||||
files: this.backupFiles.map(f => ({
|
||||
files: this.backupFiles.map((f) => ({
|
||||
name: f.name,
|
||||
type: f.type,
|
||||
path: f.path,
|
||||
size: f.size
|
||||
size: f.size,
|
||||
})),
|
||||
platform: this.platformCapabilities.isIOS ? "iOS" : "Android",
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
// Debug: Log file type distribution
|
||||
const typeCounts = {
|
||||
contacts: this.backupFiles.filter(f => f.type === 'contacts').length,
|
||||
seed: this.backupFiles.filter(f => f.type === 'seed').length,
|
||||
other: this.backupFiles.filter(f => f.type === 'other').length,
|
||||
total: this.backupFiles.length
|
||||
contacts: this.backupFiles.filter((f) => f.type === "contacts").length,
|
||||
seed: this.backupFiles.filter((f) => f.type === "seed").length,
|
||||
other: this.backupFiles.filter((f) => f.type === "other").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
|
||||
logger.log('[BackupFilesList] backupFiles array for All tab:', this.backupFiles);
|
||||
logger.log(
|
||||
"[BackupFilesList] backupFiles array for All tab:",
|
||||
this.backupFiles,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error('[BackupFilesList] Failed to refresh backup files:', error);
|
||||
logger.error("[BackupFilesList] Failed to refresh backup files:", error);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -484,12 +569,12 @@ export default class BackupFilesList extends Vue {
|
||||
async createTestBackup() {
|
||||
try {
|
||||
this.isLoading = true;
|
||||
logger.log('[BackupFilesList] Creating test backup file');
|
||||
logger.log("[BackupFilesList] Creating test backup file");
|
||||
|
||||
const result = await this.platformService.createTestBackupFile();
|
||||
|
||||
if (result.success) {
|
||||
logger.log('[BackupFilesList] Test backup file created successfully:', {
|
||||
logger.log("[BackupFilesList] Test backup file created successfully:", {
|
||||
fileName: result.fileName,
|
||||
uri: result.uri,
|
||||
timestamp: new Date().toISOString(),
|
||||
@@ -511,7 +596,10 @@ export default class BackupFilesList extends Vue {
|
||||
throw new Error(result.error || "Failed to create test backup file");
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('[BackupFilesList] Failed to create test backup file:', error);
|
||||
logger.error(
|
||||
"[BackupFilesList] Failed to create test backup file:",
|
||||
error,
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -532,11 +620,14 @@ export default class BackupFilesList extends Vue {
|
||||
async testDirectoryContexts() {
|
||||
try {
|
||||
this.isLoading = true;
|
||||
logger.log('[BackupFilesList] Testing directory contexts');
|
||||
logger.log("[BackupFilesList] Testing directory contexts");
|
||||
|
||||
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
|
||||
this.$notify(
|
||||
@@ -550,12 +641,14 @@ export default class BackupFilesList extends Vue {
|
||||
);
|
||||
|
||||
// Also log the full output to console for easy access
|
||||
console.log("=== Directory Context Test Results ===");
|
||||
console.log(debugOutput);
|
||||
console.log("=== End Test Results ===");
|
||||
|
||||
logger.log("=== Directory Context Test Results ===");
|
||||
logger.log(debugOutput);
|
||||
logger.log("=== End Test Results ===");
|
||||
} catch (error) {
|
||||
logger.error('[BackupFilesList] Failed to test directory contexts:', error);
|
||||
logger.error(
|
||||
"[BackupFilesList] Failed to test directory contexts:",
|
||||
error,
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -575,7 +668,7 @@ export default class BackupFilesList extends Vue {
|
||||
* This method can be called from parent components
|
||||
*/
|
||||
async refreshAfterSave() {
|
||||
logger.log('[BackupFilesList] refreshAfterSave called');
|
||||
logger.log("[BackupFilesList] refreshAfterSave called");
|
||||
await this.refreshFiles();
|
||||
}
|
||||
|
||||
@@ -642,14 +735,18 @@ export default class BackupFilesList extends Vue {
|
||||
* Gets the count of files for a specific type
|
||||
* 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;
|
||||
if (type === 'all') {
|
||||
if (type === "all") {
|
||||
count = this.backupFiles.length;
|
||||
logger.log('[BackupFilesList] getFileCountByType (All):', count, this.backupFiles);
|
||||
logger.log(
|
||||
"[BackupFilesList] getFileCountByType (All):",
|
||||
count,
|
||||
this.backupFiles,
|
||||
);
|
||||
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);
|
||||
return count;
|
||||
}
|
||||
@@ -659,14 +756,14 @@ export default class BackupFilesList extends Vue {
|
||||
* @param type - File type
|
||||
* @returns FontAwesome icon name
|
||||
*/
|
||||
getFileIcon(type: 'contacts' | 'seed' | 'other'): string {
|
||||
getFileIcon(type: "contacts" | "seed" | "other"): string {
|
||||
switch (type) {
|
||||
case 'contacts':
|
||||
return 'address-book';
|
||||
case 'seed':
|
||||
return 'key';
|
||||
case "contacts":
|
||||
return "address-book";
|
||||
case "seed":
|
||||
return "key";
|
||||
default:
|
||||
return 'file-alt';
|
||||
return "file-alt";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,14 +772,14 @@ export default class BackupFilesList extends Vue {
|
||||
* @param type - File type
|
||||
* @returns CSS color class
|
||||
*/
|
||||
getFileIconColor(type: 'contacts' | 'seed' | 'other'): string {
|
||||
getFileIconColor(type: "contacts" | "seed" | "other"): string {
|
||||
switch (type) {
|
||||
case 'contacts':
|
||||
return 'text-blue-500';
|
||||
case 'seed':
|
||||
return 'text-orange-500';
|
||||
case "contacts":
|
||||
return "text-blue-500";
|
||||
case "seed":
|
||||
return "text-orange-500";
|
||||
default:
|
||||
return 'text-gray-500';
|
||||
return "text-gray-500";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,14 +788,14 @@ export default class BackupFilesList extends Vue {
|
||||
* @param type - File type
|
||||
* @returns CSS color class
|
||||
*/
|
||||
getTypeBadgeColor(type: 'contacts' | 'seed' | 'other'): string {
|
||||
getTypeBadgeColor(type: "contacts" | "seed" | "other"): string {
|
||||
switch (type) {
|
||||
case 'contacts':
|
||||
return 'bg-blue-100 text-blue-800';
|
||||
case 'seed':
|
||||
return 'bg-orange-100 text-orange-800';
|
||||
case "contacts":
|
||||
return "bg-blue-100 text-blue-800";
|
||||
case "seed":
|
||||
return "bg-orange-100 text-orange-800";
|
||||
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
|
||||
*/
|
||||
formatFileSize(bytes: number): string {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
|
||||
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));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -730,38 +827,56 @@ export default class BackupFilesList extends Vue {
|
||||
|
||||
// Test listing all user files
|
||||
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
|
||||
const backupFilesResult = await platformService.testBackupFiles();
|
||||
logger.log("[BackupFilesList] Backup files test result:", backupFilesResult);
|
||||
logger.log(
|
||||
"[BackupFilesList] Backup files test result:",
|
||||
backupFilesResult,
|
||||
);
|
||||
|
||||
// Test listing all backup files (if available)
|
||||
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)
|
||||
if ('debugListAllFiles' in platformService) {
|
||||
const debugAllFiles = await (platformService as any).debugListAllFiles();
|
||||
if ("debugListAllFiles" in platformService) {
|
||||
const debugAllFiles = await (
|
||||
platformService as any
|
||||
).debugListAllFiles();
|
||||
logger.log("[BackupFilesList] Debug all files (no filtering):", {
|
||||
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)
|
||||
if ('debugFileDiscoveryStepByStep' in platformService) {
|
||||
const stepByStepDebug = await (platformService as any).debugFileDiscoveryStepByStep();
|
||||
logger.log("[BackupFilesList] Step-by-step debug output:", stepByStepDebug);
|
||||
if ("debugFileDiscoveryStepByStep" in platformService) {
|
||||
const stepByStepDebug = await (
|
||||
platformService as any
|
||||
).debugFileDiscoveryStepByStep();
|
||||
logger.log(
|
||||
"[BackupFilesList] Step-by-step debug output:",
|
||||
stepByStepDebug,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
allFiles: allFilesResult,
|
||||
backupFiles: backupFilesResult,
|
||||
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) {
|
||||
logger.error("[BackupFilesList] Debug file discovery failed:", error);
|
||||
@@ -769,7 +884,7 @@ export default class BackupFilesList extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
@Watch('platformCapabilities.hasFileSystem', { immediate: true })
|
||||
@Watch("platformCapabilities.hasFileSystem", { immediate: true })
|
||||
async onFileSystemCapabilityChanged(newVal: boolean) {
|
||||
if (newVal) {
|
||||
await this.refreshFiles();
|
||||
|
||||
@@ -44,19 +44,25 @@ explorer. * * @component * @displayName DataExportSection * @example * ```vue *
|
||||
v-if="platformCapabilities.isIOS"
|
||||
class="list-disc list-outside ml-4"
|
||||
>
|
||||
On iOS: Files are saved to Documents folder (accessible via Files app) and persist between app installations.
|
||||
On iOS: Files are saved to Documents folder (accessible via Files app)
|
||||
and persist between app installations.
|
||||
</li>
|
||||
<li
|
||||
v-if="platformCapabilities.isMobile && !platformCapabilities.isIOS"
|
||||
class="list-disc list-outside ml-4"
|
||||
>
|
||||
On Android: Files are saved to Downloads/TimeSafari or external storage (accessible via file managers) and persist between app installations.
|
||||
On Android: Files are saved to Downloads/TimeSafari or external
|
||||
storage (accessible via file managers) and persist between app
|
||||
installations.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Backup Files List -->
|
||||
<div v-if="platformCapabilities.hasFileSystem" class="mt-6 pt-6 border-t border-gray-300">
|
||||
<div
|
||||
v-if="platformCapabilities.hasFileSystem"
|
||||
class="mt-6 pt-6 border-t border-gray-300"
|
||||
>
|
||||
<BackupFilesList ref="backupFilesList" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -176,8 +182,8 @@ export default class DataExportSection extends Vue {
|
||||
{
|
||||
allowLocationSelection: true,
|
||||
showLocationSelectionDialog: true,
|
||||
mimeType: "application/json"
|
||||
}
|
||||
mimeType: "application/json",
|
||||
},
|
||||
);
|
||||
|
||||
// Handle the result
|
||||
@@ -202,7 +208,10 @@ export default class DataExportSection extends Vue {
|
||||
|
||||
// Refresh the backup files list
|
||||
const backupFilesList = this.$refs.backupFilesList as any;
|
||||
if (backupFilesList && typeof backupFilesList.refreshAfterSave === 'function') {
|
||||
if (
|
||||
backupFilesList &&
|
||||
typeof backupFilesList.refreshAfterSave === "function"
|
||||
) {
|
||||
await backupFilesList.refreshAfterSave();
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -243,7 +252,10 @@ export default class DataExportSection extends Vue {
|
||||
// Ensure permissions are requested and refresh backup files list on mount
|
||||
if (this.platformCapabilities.hasFileSystem) {
|
||||
const backupFilesList = this.$refs.backupFilesList as any;
|
||||
if (backupFilesList && typeof backupFilesList.refreshFiles === 'function') {
|
||||
if (
|
||||
backupFilesList &&
|
||||
typeof backupFilesList.refreshFiles === "function"
|
||||
) {
|
||||
await backupFilesList.refreshFiles();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export interface PlatformService {
|
||||
mimeType?: string;
|
||||
showShareDialog?: boolean;
|
||||
showLocationSelectionDialog?: boolean;
|
||||
}
|
||||
},
|
||||
): Promise<{ saved: boolean; uri?: string; shared: boolean; error?: string }>;
|
||||
|
||||
/**
|
||||
@@ -190,14 +190,24 @@ export interface PlatformService {
|
||||
* Returns files from Downloads (Android) or Documents (iOS) directories.
|
||||
* @returns Promise resolving to array of file information
|
||||
*/
|
||||
listUserAccessibleFiles(): Promise<Array<{name: string, uri: string, size?: number}>>;
|
||||
listUserAccessibleFiles(): Promise<
|
||||
Array<{ name: string; uri: string; size?: number }>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Lists backup files specifically saved by the app.
|
||||
* Filters for files that appear to be TimeSafari backups.
|
||||
* @returns Promise resolving to array of backup file information
|
||||
*/
|
||||
listBackupFiles(): Promise<Array<{name: string, uri: string, size?: number, type: 'contacts' | 'seed' | 'other', path?: string}>>;
|
||||
listBackupFiles(): Promise<
|
||||
Array<{
|
||||
name: string;
|
||||
uri: string;
|
||||
size?: number;
|
||||
type: "contacts" | "seed" | "other";
|
||||
path?: string;
|
||||
}>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Opens a file in the device's default file viewer/app.
|
||||
@@ -206,7 +216,10 @@ export interface PlatformService {
|
||||
* @param fileName - Name of the file (for display purposes)
|
||||
* @returns Promise resolving to success status
|
||||
*/
|
||||
openFile(fileUri: string, fileName: string): Promise<{ success: boolean; error?: string }>;
|
||||
openFile(
|
||||
fileUri: string,
|
||||
fileName: string,
|
||||
): Promise<{ success: boolean; error?: string }>;
|
||||
|
||||
/**
|
||||
* Opens the directory containing backup files in the device's file explorer.
|
||||
@@ -220,7 +233,12 @@ export interface PlatformService {
|
||||
* This is useful for debugging file visibility issues.
|
||||
* @returns Promise resolving to success status and file information
|
||||
*/
|
||||
createTestBackupFile(): Promise<{ success: boolean; fileName?: string; uri?: string; error?: string }>;
|
||||
createTestBackupFile(): Promise<{
|
||||
success: boolean;
|
||||
fileName?: string;
|
||||
uri?: string;
|
||||
error?: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Tests different directory contexts to see what files are available.
|
||||
@@ -235,7 +253,18 @@ export interface PlatformService {
|
||||
* @param debugShowAll - Debug flag to treat all entries as files
|
||||
* @returns Promise resolving to array of directory entries
|
||||
*/
|
||||
listFilesInDirectory(path: string, debugShowAll?: boolean): Promise<Array<{name: string, uri: string, size?: number, path: string, type: 'file' | 'folder'}>>;
|
||||
listFilesInDirectory(
|
||||
path: string,
|
||||
debugShowAll?: boolean,
|
||||
): Promise<
|
||||
Array<{
|
||||
name: string;
|
||||
uri: string;
|
||||
size?: number;
|
||||
path: string;
|
||||
type: "file" | "folder";
|
||||
}>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Debug method to check what's actually in the TimeSafari directory
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -249,12 +249,17 @@ export class ElectronPlatformService implements PlatformService {
|
||||
mimeType?: string;
|
||||
showShareDialog?: boolean;
|
||||
showLocationSelectionDialog?: boolean;
|
||||
}
|
||||
): Promise<{ saved: boolean; uri?: string; shared: boolean; error?: string }> {
|
||||
},
|
||||
): Promise<{
|
||||
saved: boolean;
|
||||
uri?: string;
|
||||
shared: boolean;
|
||||
error?: string;
|
||||
}> {
|
||||
return {
|
||||
saved: false,
|
||||
shared: false,
|
||||
error: "Not implemented in Electron platform"
|
||||
error: "Not implemented in Electron platform",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -435,7 +440,9 @@ export class ElectronPlatformService implements PlatformService {
|
||||
* Not implemented in Electron platform.
|
||||
* @returns Promise resolving to empty array
|
||||
*/
|
||||
async listUserAccessibleFiles(): Promise<Array<{name: string, uri: string, size?: number}>> {
|
||||
async listUserAccessibleFiles(): Promise<
|
||||
Array<{ name: string; uri: string; size?: number }>
|
||||
> {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -444,7 +451,15 @@ export class ElectronPlatformService implements PlatformService {
|
||||
* Not implemented for Electron platform.
|
||||
* @returns Promise resolving to empty array
|
||||
*/
|
||||
async listBackupFiles(): Promise<Array<{name: string, uri: string, size?: number, type: 'contacts' | 'seed' | 'other', path?: string}>> {
|
||||
async listBackupFiles(): Promise<
|
||||
Array<{
|
||||
name: string;
|
||||
uri: string;
|
||||
size?: number;
|
||||
type: "contacts" | "seed" | "other";
|
||||
path?: string;
|
||||
}>
|
||||
> {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -455,8 +470,14 @@ export class ElectronPlatformService implements PlatformService {
|
||||
* @param _fileName - Name of the file (for display purposes)
|
||||
* @returns Promise resolving to error status
|
||||
*/
|
||||
async openFile(_fileUri: string, _fileName: string): Promise<{ success: boolean; error?: string }> {
|
||||
return { success: false, error: "File opening not implemented in Electron platform" };
|
||||
async openFile(
|
||||
_fileUri: string,
|
||||
_fileName: string,
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
return {
|
||||
success: false,
|
||||
error: "File opening not implemented in Electron platform",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,7 +486,10 @@ export class ElectronPlatformService implements PlatformService {
|
||||
* @returns Promise resolving to error status
|
||||
*/
|
||||
async openBackupDirectory(): Promise<{ success: boolean; error?: string }> {
|
||||
return { success: false, error: "Directory access not implemented in Electron platform" };
|
||||
return {
|
||||
success: false,
|
||||
error: "Directory access not implemented in Electron platform",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -473,7 +497,18 @@ export class ElectronPlatformService implements PlatformService {
|
||||
* Not implemented for Electron platform.
|
||||
* @returns Promise resolving to empty array
|
||||
*/
|
||||
async listFilesInDirectory(path: string, debugShowAll?: boolean): Promise<Array<{name: string, uri: string, size?: number, path: string, type: 'file' | 'folder'}>> {
|
||||
async listFilesInDirectory(
|
||||
_path: string,
|
||||
_debugShowAll?: boolean,
|
||||
): Promise<
|
||||
Array<{
|
||||
name: string;
|
||||
uri: string;
|
||||
size?: number;
|
||||
path: string;
|
||||
type: "file" | "folder";
|
||||
}>
|
||||
> {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -491,10 +526,16 @@ export class ElectronPlatformService implements PlatformService {
|
||||
* Not implemented for Electron platform.
|
||||
* @returns Promise resolving to error status
|
||||
*/
|
||||
async createTestBackupFile(): Promise<{ success: boolean; fileName?: string; uri?: string; error?: string }> {
|
||||
async createTestBackupFile(): Promise<{
|
||||
success: boolean;
|
||||
fileName?: string;
|
||||
uri?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
return {
|
||||
success: false,
|
||||
error: "Electron platform does not support file system access for creating test backup files."
|
||||
error:
|
||||
"Electron platform does not support file system access for creating test backup files.",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -140,9 +140,18 @@ export class PyWebViewPlatformService implements PlatformService {
|
||||
mimeType?: string;
|
||||
showShareDialog?: boolean;
|
||||
showLocationSelectionDialog?: boolean;
|
||||
}
|
||||
): Promise<{ saved: boolean; uri?: string; shared: boolean; error?: string }> {
|
||||
return { saved: false, shared: false, error: "File sharing not implemented in PyWebView platform" };
|
||||
},
|
||||
): Promise<{
|
||||
saved: boolean;
|
||||
uri?: string;
|
||||
shared: boolean;
|
||||
error?: string;
|
||||
}> {
|
||||
return {
|
||||
saved: false,
|
||||
shared: false,
|
||||
error: "File sharing not implemented in PyWebView platform",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +159,9 @@ export class PyWebViewPlatformService implements PlatformService {
|
||||
* Not implemented in PyWebView platform.
|
||||
* @returns Promise resolving to empty array
|
||||
*/
|
||||
async listUserAccessibleFiles(): Promise<Array<{name: string, uri: string, size?: number}>> {
|
||||
async listUserAccessibleFiles(): Promise<
|
||||
Array<{ name: string; uri: string; size?: number }>
|
||||
> {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -159,7 +170,15 @@ export class PyWebViewPlatformService implements PlatformService {
|
||||
* Not implemented for PyWebView platform.
|
||||
* @returns Promise resolving to empty array
|
||||
*/
|
||||
async listBackupFiles(): Promise<Array<{name: string, uri: string, size?: number, type: 'contacts' | 'seed' | 'other', path?: string}>> {
|
||||
async listBackupFiles(): Promise<
|
||||
Array<{
|
||||
name: string;
|
||||
uri: string;
|
||||
size?: number;
|
||||
type: "contacts" | "seed" | "other";
|
||||
path?: string;
|
||||
}>
|
||||
> {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -170,8 +189,14 @@ export class PyWebViewPlatformService implements PlatformService {
|
||||
* @param _fileName - Name of the file (for display purposes)
|
||||
* @returns Promise resolving to error status
|
||||
*/
|
||||
async openFile(_fileUri: string, _fileName: string): Promise<{ success: boolean; error?: string }> {
|
||||
return { success: false, error: "File opening not implemented in PyWebView platform" };
|
||||
async openFile(
|
||||
_fileUri: string,
|
||||
_fileName: string,
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
return {
|
||||
success: false,
|
||||
error: "File opening not implemented in PyWebView platform",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +205,10 @@ export class PyWebViewPlatformService implements PlatformService {
|
||||
* @returns Promise resolving to error status
|
||||
*/
|
||||
async openBackupDirectory(): Promise<{ success: boolean; error?: string }> {
|
||||
return { success: false, error: "Directory access not implemented in PyWebView platform" };
|
||||
return {
|
||||
success: false,
|
||||
error: "Directory access not implemented in PyWebView platform",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +280,18 @@ export class PyWebViewPlatformService implements PlatformService {
|
||||
* Not implemented for PyWebView platform.
|
||||
* @returns Promise resolving to empty array
|
||||
*/
|
||||
async listFilesInDirectory(path: string, debugShowAll?: boolean): Promise<Array<{name: string, uri: string, size?: number, path: string, type: 'file' | 'folder'}>> {
|
||||
async listFilesInDirectory(
|
||||
_path: string,
|
||||
_debugShowAll?: boolean,
|
||||
): Promise<
|
||||
Array<{
|
||||
name: string;
|
||||
uri: string;
|
||||
size?: number;
|
||||
path: string;
|
||||
type: "file" | "folder";
|
||||
}>
|
||||
> {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -270,10 +309,16 @@ export class PyWebViewPlatformService implements PlatformService {
|
||||
* Not implemented for PyWebView platform.
|
||||
* @returns Promise resolving to error status
|
||||
*/
|
||||
async createTestBackupFile(): Promise<{ success: boolean; fileName?: string; uri?: string; error?: string }> {
|
||||
async createTestBackupFile(): Promise<{
|
||||
success: boolean;
|
||||
fileName?: string;
|
||||
uri?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
return {
|
||||
success: false,
|
||||
error: "PyWebView platform does not support file system access for creating test backup files."
|
||||
error:
|
||||
"PyWebView platform does not support file system access for creating test backup files.",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,10 @@ export class WebPlatformService implements PlatformService {
|
||||
return {
|
||||
hasFileSystem: false,
|
||||
hasCamera: true, // Through file input with capture
|
||||
isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
||||
navigator.userAgent,
|
||||
),
|
||||
isMobile:
|
||||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
||||
navigator.userAgent,
|
||||
),
|
||||
isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent),
|
||||
hasFileDownload: true,
|
||||
needsFileHandlingInstructions: false,
|
||||
@@ -372,12 +373,17 @@ export class WebPlatformService implements PlatformService {
|
||||
mimeType?: string;
|
||||
showShareDialog?: boolean;
|
||||
showLocationSelectionDialog?: boolean;
|
||||
}
|
||||
): Promise<{ saved: boolean; uri?: string; shared: boolean; error?: string }> {
|
||||
},
|
||||
): Promise<{
|
||||
saved: boolean;
|
||||
uri?: string;
|
||||
shared: boolean;
|
||||
error?: string;
|
||||
}> {
|
||||
return {
|
||||
saved: false,
|
||||
shared: false,
|
||||
error: "File system access not available in web platform"
|
||||
error: "File system access not available in web platform",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -471,7 +477,9 @@ export class WebPlatformService implements PlatformService {
|
||||
* Not supported in web platform.
|
||||
* @returns Promise resolving to empty array
|
||||
*/
|
||||
async listUserAccessibleFiles(): Promise<Array<{name: string, uri: string, size?: number}>> {
|
||||
async listUserAccessibleFiles(): Promise<
|
||||
Array<{ name: string; uri: string; size?: number }>
|
||||
> {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -480,7 +488,15 @@ export class WebPlatformService implements PlatformService {
|
||||
* Not supported in web platform.
|
||||
* @returns Promise resolving to empty array
|
||||
*/
|
||||
async listBackupFiles(): Promise<Array<{name: string, uri: string, size?: number, type: 'contacts' | 'seed' | 'other', path?: string}>> {
|
||||
async listBackupFiles(): Promise<
|
||||
Array<{
|
||||
name: string;
|
||||
uri: string;
|
||||
size?: number;
|
||||
type: "contacts" | "seed" | "other";
|
||||
path?: string;
|
||||
}>
|
||||
> {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -491,8 +507,14 @@ export class WebPlatformService implements PlatformService {
|
||||
* @param _fileName - Name of the file (for display purposes)
|
||||
* @returns Promise resolving to error status
|
||||
*/
|
||||
async openFile(_fileUri: string, _fileName: string): Promise<{ success: boolean; error?: string }> {
|
||||
return { success: false, error: "File opening not available in web platform" };
|
||||
async openFile(
|
||||
_fileUri: string,
|
||||
_fileName: string,
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
return {
|
||||
success: false,
|
||||
error: "File opening not available in web platform",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -501,7 +523,10 @@ export class WebPlatformService implements PlatformService {
|
||||
* @returns Promise resolving to error status
|
||||
*/
|
||||
async openBackupDirectory(): Promise<{ success: boolean; error?: string }> {
|
||||
return { success: false, error: "Directory access not available in web platform" };
|
||||
return {
|
||||
success: false,
|
||||
error: "Directory access not available in web platform",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,7 +544,18 @@ export class WebPlatformService implements PlatformService {
|
||||
* Not supported in web platform.
|
||||
* @returns Promise resolving to empty array
|
||||
*/
|
||||
async listFilesInDirectory(path: string, debugShowAll?: boolean): Promise<Array<{name: string, uri: string, size?: number, path: string, type: 'file' | 'folder'}>> {
|
||||
async listFilesInDirectory(
|
||||
_path: string,
|
||||
_debugShowAll?: boolean,
|
||||
): Promise<
|
||||
Array<{
|
||||
name: string;
|
||||
uri: string;
|
||||
size?: number;
|
||||
path: string;
|
||||
type: "file" | "folder";
|
||||
}>
|
||||
> {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -537,10 +573,16 @@ export class WebPlatformService implements PlatformService {
|
||||
* Not supported in web platform.
|
||||
* @returns Promise resolving to error status
|
||||
*/
|
||||
async createTestBackupFile(): Promise<{ success: boolean; fileName?: string; uri?: string; error?: string }> {
|
||||
async createTestBackupFile(): Promise<{
|
||||
success: boolean;
|
||||
fileName?: string;
|
||||
uri?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
return {
|
||||
success: false,
|
||||
error: "Web platform does not support file system access for creating test backup files."
|
||||
error:
|
||||
"Web platform does not support file system access for creating test backup files.",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ export default { logger };
|
||||
* @returns Formatted timestamp string safe for filenames
|
||||
*/
|
||||
export function getTimestampForFilename(): string {
|
||||
return new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
||||
return new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -217,7 +217,8 @@
|
||||
|
||||
<div class="mt-8">
|
||||
<h2 class="text-xl font-bold mb-4">File Sharing Test</h2>
|
||||
Test the new file sharing functionality that saves to user-accessible locations.
|
||||
Test the new file sharing functionality that saves to user-accessible
|
||||
locations.
|
||||
<div>
|
||||
<button
|
||||
class="font-bold capitalize bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
|
||||
@@ -747,7 +748,10 @@ export default class Help extends Vue {
|
||||
try {
|
||||
const result = await platformService.testLocationSelectionSilent();
|
||||
this.fileSharingResult = result;
|
||||
logger.log("Silent Location Selection Test Result:", this.fileSharingResult);
|
||||
logger.log(
|
||||
"Silent Location Selection Test Result:",
|
||||
this.fileSharingResult,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error("Silent Location Selection Test Error:", error);
|
||||
this.$notify(
|
||||
@@ -825,8 +829,10 @@ export default class Help extends Vue {
|
||||
public async testFileDiscoveryDebug() {
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
try {
|
||||
if ('debugFileDiscoveryStepByStep' in platformService) {
|
||||
const result = await (platformService as any).debugFileDiscoveryStepByStep();
|
||||
if ("debugFileDiscoveryStepByStep" in platformService) {
|
||||
const result = await (
|
||||
platformService as any
|
||||
).debugFileDiscoveryStepByStep();
|
||||
this.fileSharingResult = result;
|
||||
logger.log("File Discovery Debug Test Result:", this.fileSharingResult);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user