From 07fd2b4d2c0abbf5c362ca70d8d7a057843416c1 Mon Sep 17 00:00:00 2001 From: Tony Chen Date: Tue, 10 Feb 2026 21:12:09 +1100 Subject: [PATCH 1/2] Remove hard-coded path names related to HealthPod --- lib/src/models/data_format_config.dart | 42 ----- lib/src/models/file_type_config.dart | 161 ++++++------------ lib/src/widgets/solid_file.dart | 26 +++ lib/src/widgets/solid_file_browser.dart | 10 ++ .../widgets/solid_file_browser_builder.dart | 2 + lib/src/widgets/solid_file_helpers.dart | 82 +++++---- lib/src/widgets/solid_file_operations.dart | 59 +++---- 7 files changed, 153 insertions(+), 229 deletions(-) diff --git a/lib/src/models/data_format_config.dart b/lib/src/models/data_format_config.dart index a0c53dd..0e3c223 100644 --- a/lib/src/models/data_format_config.dart +++ b/lib/src/models/data_format_config.dart @@ -97,45 +97,3 @@ class DataFormatConfig { description.hashCode; } } - -/// Pre-defined format configurations for common health data types. - -class SolidFileDataFormats { - static const bloodPressure = DataFormatConfig( - title: 'Blood Pressure CSV Format', - requiredFields: ['timestamp', 'systolic', 'diastolic', 'heart_rate'], - optionalFields: ['notes'], - ); - - static const vaccination = DataFormatConfig( - title: 'Vaccination CSV Format', - requiredFields: ['timestamp', 'name', 'type'], - optionalFields: ['location', 'notes', 'batch_number'], - ); - - static const medication = DataFormatConfig( - title: 'Medication CSV Format', - requiredFields: ['timestamp', 'name', 'dosage', 'frequency', 'start_date'], - optionalFields: ['notes'], - ); - - static const diary = DataFormatConfig( - title: 'Appointment CSV Format', - requiredFields: ['timestamp', 'content'], - optionalFields: ['mood', 'tags', 'notes'], - ); - - static const profile = DataFormatConfig( - title: 'Profile JSON Format', - requiredFields: [ - 'name', - 'address', - 'bestContactPhone', - 'alternativeContactNumber', - 'email', - 'dateOfBirth', - 'gender', - ], - isJson: true, - ); -} diff --git a/lib/src/models/file_type_config.dart b/lib/src/models/file_type_config.dart index 3ad21ba..eda4b08 100644 --- a/lib/src/models/file_type_config.dart +++ b/lib/src/models/file_type_config.dart @@ -33,23 +33,25 @@ import 'package:solidui/src/utils/path_utils.dart'; import 'package:solidui/src/widgets/solid_file_helpers.dart'; import 'package:solidui/src/widgets/solid_file_upload_config.dart'; -/// Predefined file types for different data categories. - -enum SolidFileType { - bloodPressure, - vaccination, - medication, - diary, - profile, - general, -} +/// A callback type for resolving file type configurations from a path. +/// +/// Applications can provide their own resolver to map directory paths to +/// specific [FileTypeConfig] instances. Return `null` to fall through to the +/// default generic behaviour. + +typedef FileTypeResolver = FileTypeConfig? Function( + String normalisedPath, + String? basePath, +); /// Configuration for different file types. class FileTypeConfig { - /// The file type. + /// A string identifier for this file type (e.g. 'general', 'blood_pressure'). + /// + /// Applications may define their own type identifiers. - final SolidFileType type; + final String typeId; /// Display name for the folder. @@ -84,7 +86,7 @@ class FileTypeConfig { final String uploadTooltip; const FileTypeConfig({ - required this.type, + this.typeId = 'general', required this.displayName, this.showCsvButtons = false, this.showProfileButtons = false, @@ -97,121 +99,58 @@ class FileTypeConfig { /// Gets the file type configuration based on the current path. - static FileTypeConfig fromPath(String currentPath, [String? basePath]) { + static FileTypeConfig fromPath( + String currentPath, [ + String? basePath, + FileTypeResolver? resolver, + ]) { // Normalise the path for consistent pattern matching. final normalisedPath = PathUtils.normalise(currentPath); - if (normalisedPath.contains('/blood_pressure') || - normalisedPath.contains('blood_pressure/') || - normalisedPath.endsWith('blood_pressure')) { - return const FileTypeConfig( - type: SolidFileType.bloodPressure, - displayName: 'Blood Pressure Data', - showCsvButtons: true, - formatConfig: SolidFileDataFormats.bloodPressure, - uploadTooltip: ''' + // Attempt app-specific resolution first. -**Upload**: Tap here to upload a file to your Solid Health Pod. + if (resolver != null) { + final resolved = resolver(normalisedPath, basePath); + if (resolved != null) return resolved; + } -''', - ); - } else if (normalisedPath.contains('/vaccination') || - normalisedPath.contains('vaccination/') || - normalisedPath.endsWith('vaccination')) { - return const FileTypeConfig( - type: SolidFileType.vaccination, - displayName: 'Vaccination Data', - showCsvButtons: true, - formatConfig: SolidFileDataFormats.vaccination, - uploadTooltip: ''' - -**Upload**: Tap here to upload a file to your Solid Health Pod. + // Generic fallback — derive a friendly display name from the path. -''', - ); - } else if (normalisedPath.contains('/medication') || - normalisedPath.contains('medication/') || - normalisedPath.endsWith('medication')) { - return const FileTypeConfig( - type: SolidFileType.medication, - displayName: 'Medication Data', - showCsvButtons: true, - formatConfig: SolidFileDataFormats.medication, - uploadTooltip: ''' - -**Upload**: Tap here to upload a file to your Solid Health Pod. + String effectiveBasePath = + basePath != null ? PathUtils.normalise(basePath) : ''; -''', - ); - } else if (normalisedPath.contains('/diary') || - normalisedPath.contains('diary/') || - normalisedPath.endsWith('diary')) { - return const FileTypeConfig( - type: SolidFileType.diary, - displayName: 'Appointments Data', - showCsvButtons: true, - formatConfig: SolidFileDataFormats.diary, - uploadTooltip: ''' - -**Upload**: Tap here to upload a file to your Solid Health Pod. + if (effectiveBasePath.isEmpty) { + final segments = + normalisedPath.split('/').where((s) => s.isNotEmpty).toList(); -''', - ); - } else if (normalisedPath.contains('/profile') || - normalisedPath.contains('profile/') || - normalisedPath.endsWith('profile')) { - return const FileTypeConfig( - type: SolidFileType.profile, - displayName: 'Profile Data', - showProfileButtons: true, - formatConfig: SolidFileDataFormats.profile, - uploadTooltip: ''' - -**Upload**: Tap here to upload a file to your Solid Health Pod. + // Construct a reasonable base path — typically the first 2 segments + // for most cases. -''', - ); - } else { - // General case - use the existing friendly folder name logic for - // consistency. If basePath is provided, use it; otherwise, construct a - // reasonable default. - - String effectiveBasePath = - basePath != null ? PathUtils.normalise(basePath) : ''; - - if (effectiveBasePath.isEmpty) { - final segments = - normalisedPath.split('/').where((s) => s.isNotEmpty).toList(); - - // Construct a reasonable base path - typically the first 2 segments - // for most cases. - - if (segments.length >= 2) { - effectiveBasePath = '${segments[0]}/${segments[1]}'; - } else if (segments.length == 1) { - effectiveBasePath = segments[0]; - } + if (segments.length >= 2) { + effectiveBasePath = '${segments[0]}/${segments[1]}'; + } else if (segments.length == 1) { + effectiveBasePath = segments[0]; } + } - final friendlyName = SolidFileHelpers.getFriendlyFolderName( - normalisedPath, - effectiveBasePath, - ); + final friendlyName = SolidFileHelpers.getFriendlyFolderName( + normalisedPath, + effectiveBasePath, + ); - String displayName = - friendlyName == 'Home' ? 'Home Folder' : friendlyName; + String displayName = + friendlyName == 'Home' ? 'Home Folder' : friendlyName; - return FileTypeConfig( - type: SolidFileType.general, - displayName: displayName, - uploadTooltip: ''' + return FileTypeConfig( + typeId: 'general', + displayName: displayName, + uploadTooltip: ''' -**Upload**: Tap here to upload a file to your Solid Health Pod. +**Upload**: Tap here to upload a file to your Solid Pod. ''', - ); - } + ); } /// Creates the upload configuration for this file type. diff --git a/lib/src/widgets/solid_file.dart b/lib/src/widgets/solid_file.dart index 4c2e3f4..bf85f34 100644 --- a/lib/src/widgets/solid_file.dart +++ b/lib/src/widgets/solid_file.dart @@ -32,6 +32,7 @@ import 'package:flutter/material.dart'; import 'package:solidpod/solidpod.dart' show getDataDirPath; +import 'package:solidui/src/models/file_type_config.dart'; import 'package:solidui/src/utils/path_utils.dart'; import 'package:solidui/src/widgets/solid_file_browser.dart'; import 'package:solidui/src/widgets/solid_file_browser_builder.dart'; @@ -123,6 +124,23 @@ class SolidFile extends StatefulWidget { final bool autoConfig; + /// Optional resolver for mapping paths to file type configurations. + /// + /// Applications can supply their own resolver to provide custom upload + /// configurations, display names, and format configs based on the current + /// directory path. When the resolver returns `null`, the generic default + /// behaviour is used. + + final FileTypeResolver? fileTypeResolver; + + /// Optional map of directory basenames to display names. + /// + /// When provided, the file browser uses these overrides to display + /// user-friendly folder names (e.g. `{'blood_pressure': 'Blood Pressure + /// Data'}`). Entries not found in the map fall back to generic formatting. + + final Map? folderNameOverrides; + const SolidFile({ super.key, this.currentPath, @@ -144,6 +162,8 @@ class SolidFile extends StatefulWidget { this.uploadState, this.browserKey, this.autoConfig = true, + this.fileTypeResolver, + this.folderNameOverrides, }); /// Legacy constructor for backward compatibility. @@ -154,6 +174,8 @@ class SolidFile extends StatefulWidget { required SolidFileCallbacks callbacks, required SolidFileState state, this.browserKey, + this.fileTypeResolver, + this.folderNameOverrides, }) : currentPath = state.currentPath, friendlyFolderName = state.friendlyFolderName, showBackButton = config.showBackButton, @@ -355,6 +377,7 @@ class _SolidFileState extends State { widget.autoConfig, widget.showUpload, widget.uploadConfig, + widget.fileTypeResolver, ), uploadCallbacks: _getEffectiveUploadCallbacks(), uploadState: widget.uploadState ?? @@ -372,6 +395,7 @@ class _SolidFileState extends State { widget.autoConfig, widget.showUpload, widget.uploadConfig, + widget.fileTypeResolver, ), uploadCallbacks: _getEffectiveUploadCallbacks(), uploadState: widget.uploadState ?? @@ -400,6 +424,7 @@ class _SolidFileState extends State { _effectiveBasePath, widget.autoConfig, widget.friendlyFolderName, + widget.fileTypeResolver, ), initialPath: widget.currentPath, onFileSelected: widget.onFileSelected, @@ -408,6 +433,7 @@ class _SolidFileState extends State { onImportCsv: widget.onImportCsv, onDirectoryChanged: _handleDirectoryChanged, uploadCallbacks: widget.uploadCallbacks, + folderNameOverrides: widget.folderNameOverrides, ); } } diff --git a/lib/src/widgets/solid_file_browser.dart b/lib/src/widgets/solid_file_browser.dart index fd43a5f..e0f937b 100644 --- a/lib/src/widgets/solid_file_browser.dart +++ b/lib/src/widgets/solid_file_browser.dart @@ -81,6 +81,14 @@ class SolidFileBrowser extends StatefulWidget { final String? initialPath; + /// Optional map of directory basenames to display names. + /// + /// When provided, these overrides are used to display user-friendly folder + /// names in the path bar. Entries not found in the map fall back to generic + /// formatting. + + final Map? folderNameOverrides; + const SolidFileBrowser({ super.key, required this.onFileSelected, @@ -91,6 +99,7 @@ class SolidFileBrowser extends StatefulWidget { required this.onDirectoryChanged, required this.friendlyFolderName, this.initialPath, + this.folderNameOverrides, }); @override @@ -348,6 +357,7 @@ class SolidFileBrowserState extends State { return SolidFileOperations.getFriendlyFolderName( currentPath, _homePath, + widget.folderNameOverrides, ); } diff --git a/lib/src/widgets/solid_file_browser_builder.dart b/lib/src/widgets/solid_file_browser_builder.dart index 539df14..c1efd35 100644 --- a/lib/src/widgets/solid_file_browser_builder.dart +++ b/lib/src/widgets/solid_file_browser_builder.dart @@ -49,12 +49,14 @@ class SolidFileBrowserBuilder { Function(String fileName, String filePath)? onImportCsv, required Function(String path) onDirectoryChanged, SolidFileUploadCallbacks? uploadCallbacks, + Map? folderNameOverrides, }) { return SolidFileBrowser( key: browserKey, browserKey: browserKey, friendlyFolderName: friendlyFolderName, initialPath: initialPath, + folderNameOverrides: folderNameOverrides, onFileSelected: onFileSelected ?? (fileName, filePath) { debugPrint('File selected: $fileName at $filePath'); diff --git a/lib/src/widgets/solid_file_helpers.dart b/lib/src/widgets/solid_file_helpers.dart index 837a552..4182eec 100644 --- a/lib/src/widgets/solid_file_helpers.dart +++ b/lib/src/widgets/solid_file_helpers.dart @@ -65,8 +65,9 @@ class SolidFileHelpers { String basePath, bool autoConfig, bool showUpload, - SolidFileUploadConfig? uploadConfig, - ) { + SolidFileUploadConfig? uploadConfig, [ + FileTypeResolver? fileTypeResolver, + ]) { if (uploadConfig != null) { return uploadConfig; } @@ -76,8 +77,11 @@ class SolidFileHelpers { final normalisedCurrentPath = PathUtils.normalise(currentPath); final normalisedBasePath = PathUtils.normalise(basePath); - final typeConfig = - FileTypeConfig.fromPath(normalisedCurrentPath, normalisedBasePath); + final typeConfig = FileTypeConfig.fromPath( + normalisedCurrentPath, + normalisedBasePath, + fileTypeResolver, + ); return typeConfig.createUploadConfig(); } @@ -91,8 +95,9 @@ class SolidFileHelpers { String currentPath, String basePath, bool autoConfig, - String? friendlyFolderName, - ) { + String? friendlyFolderName, [ + FileTypeResolver? fileTypeResolver, + ]) { if (friendlyFolderName != null) { return friendlyFolderName; } @@ -102,8 +107,11 @@ class SolidFileHelpers { final normalisedCurrentPath = PathUtils.normalise(currentPath); final normalisedBasePath = PathUtils.normalise(basePath); - final typeConfig = - FileTypeConfig.fromPath(normalisedCurrentPath, normalisedBasePath); + final typeConfig = FileTypeConfig.fromPath( + normalisedCurrentPath, + normalisedBasePath, + fileTypeResolver, + ); return typeConfig.displayName; } @@ -112,7 +120,11 @@ class SolidFileHelpers { /// Helper function to get a user-friendly name from the path. - static String getFriendlyFolderName(String pathValue, String basePath) { + static String getFriendlyFolderName( + String pathValue, + String basePath, [ + Map? folderNameOverrides, + ]) { // Normalise paths for consistent comparison. final normalisedPath = PathUtils.normalise(pathValue); @@ -125,39 +137,25 @@ class SolidFileHelpers { final dirName = path.basename(normalisedPath); - switch (dirName) { - case 'diary': - return 'Appointments Data'; - case 'blood_pressure': - return 'Blood Pressure Data'; - case 'medication': - return 'Medication Data'; - case 'vaccination': - return 'Vaccination Data'; - case 'profile': - return 'Profile Data'; - case 'health_plan': - return 'Health Plan Data'; - case 'pathology': - return 'Pathology Data'; - case 'tv_shows': - return 'TV Shows'; - - default: - // Basic formatting for unknown folders: - // capitalise first letter, replace underscores. - - if (dirName.isEmpty) return 'Folder'; - String formattedName = dirName.replaceAll('_', ' ').trim(); - formattedName = formattedName - .split(RegExp(r'\s+')) - .map( - (w) => w.isEmpty - ? w - : '${w[0].toUpperCase()}${w.substring(1).toLowerCase()}', - ) - .join(' '); - return formattedName; + // Check application-specific overrides first. + + if (folderNameOverrides != null && folderNameOverrides.containsKey(dirName)) { + return folderNameOverrides[dirName]!; } + + // Generic formatting for all folders: + // capitalise first letter, replace underscores. + + if (dirName.isEmpty) return 'Folder'; + String formattedName = dirName.replaceAll('_', ' ').trim(); + formattedName = formattedName + .split(RegExp(r'\s+')) + .map( + (w) => w.isEmpty + ? w + : '${w[0].toUpperCase()}${w.substring(1).toLowerCase()}', + ) + .join(' '); + return formattedName; } } diff --git a/lib/src/widgets/solid_file_operations.dart b/lib/src/widgets/solid_file_operations.dart index 153e103..c7b427d 100644 --- a/lib/src/widgets/solid_file_operations.dart +++ b/lib/src/widgets/solid_file_operations.dart @@ -235,7 +235,11 @@ class SolidFileOperations { /// Helper function to get a user-friendly name from the path. - static String getFriendlyFolderName(String pathValue, String basePath) { + static String getFriendlyFolderName( + String pathValue, + String basePath, [ + Map? folderNameOverrides, + ]) { final String root = basePath; if (pathValue.isEmpty || pathValue == root) { return 'Home Folder'; @@ -245,40 +249,27 @@ class SolidFileOperations { final dirName = path.basename(pathValue); - switch (dirName) { - case 'diary': - return 'Appointments Data'; - case 'blood_pressure': - return 'Blood Pressure Data'; - case 'medication': - return 'Medication Data'; - case 'vaccination': - return 'Vaccination Data'; - case 'profile': - return 'Profile Data'; - case 'health_plan': - return 'Health Plan Data'; - case 'pathology': - return 'Pathology Data'; - case 'tv_shows': - return 'TV Shows'; - - default: - // Basic formatting for unknown folders: - // capitalise first letter, replace underscores. - - if (dirName.isEmpty) return 'Folder'; - String formattedName = dirName.replaceAll('_', ' ').trim(); - formattedName = formattedName - .split(RegExp(r'\s+')) - .map( - (w) => w.isEmpty - ? w - : '${w[0].toUpperCase()}${w.substring(1).toLowerCase()}', - ) - .join(' '); - return formattedName; + // Check application-specific overrides first. + + if (folderNameOverrides != null && + folderNameOverrides.containsKey(dirName)) { + return folderNameOverrides[dirName]!; } + + // Generic formatting for all folders: + // capitalise first letter, replace underscores. + + if (dirName.isEmpty) return 'Folder'; + String formattedName = dirName.replaceAll('_', ' ').trim(); + formattedName = formattedName + .split(RegExp(r'\s+')) + .map( + (w) => w.isEmpty + ? w + : '${w[0].toUpperCase()}${w.substring(1).toLowerCase()}', + ) + .join(' '); + return formattedName; } /// Shows an alert dialog with the given message. From 75f41bc6a8a31a3eb3f27e548a2a3f7eac79d025 Mon Sep 17 00:00:00 2001 From: Tony Chen Date: Tue, 10 Feb 2026 21:51:48 +1100 Subject: [PATCH 2/2] Lint --- lib/src/models/file_type_config.dart | 3 +-- lib/src/widgets/solid_file_helpers.dart | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/models/file_type_config.dart b/lib/src/models/file_type_config.dart index eda4b08..9b91f11 100644 --- a/lib/src/models/file_type_config.dart +++ b/lib/src/models/file_type_config.dart @@ -139,8 +139,7 @@ class FileTypeConfig { effectiveBasePath, ); - String displayName = - friendlyName == 'Home' ? 'Home Folder' : friendlyName; + String displayName = friendlyName == 'Home' ? 'Home Folder' : friendlyName; return FileTypeConfig( typeId: 'general', diff --git a/lib/src/widgets/solid_file_helpers.dart b/lib/src/widgets/solid_file_helpers.dart index 4182eec..992c22e 100644 --- a/lib/src/widgets/solid_file_helpers.dart +++ b/lib/src/widgets/solid_file_helpers.dart @@ -139,7 +139,8 @@ class SolidFileHelpers { // Check application-specific overrides first. - if (folderNameOverrides != null && folderNameOverrides.containsKey(dirName)) { + if (folderNameOverrides != null && + folderNameOverrides.containsKey(dirName)) { return folderNameOverrides[dirName]!; }