Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 0 additions & 42 deletions lib/src/models/data_format_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}
160 changes: 49 additions & 111 deletions lib/src/models/file_type_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -97,121 +99,57 @@ 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.
Expand Down
26 changes: 26 additions & 0 deletions lib/src/widgets/solid_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<String, String>? folderNameOverrides;

const SolidFile({
super.key,
this.currentPath,
Expand All @@ -144,6 +162,8 @@ class SolidFile extends StatefulWidget {
this.uploadState,
this.browserKey,
this.autoConfig = true,
this.fileTypeResolver,
this.folderNameOverrides,
});

/// Legacy constructor for backward compatibility.
Expand All @@ -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,
Expand Down Expand Up @@ -355,6 +377,7 @@ class _SolidFileState extends State<SolidFile> {
widget.autoConfig,
widget.showUpload,
widget.uploadConfig,
widget.fileTypeResolver,
),
uploadCallbacks: _getEffectiveUploadCallbacks(),
uploadState: widget.uploadState ??
Expand All @@ -372,6 +395,7 @@ class _SolidFileState extends State<SolidFile> {
widget.autoConfig,
widget.showUpload,
widget.uploadConfig,
widget.fileTypeResolver,
),
uploadCallbacks: _getEffectiveUploadCallbacks(),
uploadState: widget.uploadState ??
Expand Down Expand Up @@ -400,6 +424,7 @@ class _SolidFileState extends State<SolidFile> {
_effectiveBasePath,
widget.autoConfig,
widget.friendlyFolderName,
widget.fileTypeResolver,
),
initialPath: widget.currentPath,
onFileSelected: widget.onFileSelected,
Expand All @@ -408,6 +433,7 @@ class _SolidFileState extends State<SolidFile> {
onImportCsv: widget.onImportCsv,
onDirectoryChanged: _handleDirectoryChanged,
uploadCallbacks: widget.uploadCallbacks,
folderNameOverrides: widget.folderNameOverrides,
);
}
}
10 changes: 10 additions & 0 deletions lib/src/widgets/solid_file_browser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String>? folderNameOverrides;

const SolidFileBrowser({
super.key,
required this.onFileSelected,
Expand All @@ -91,6 +99,7 @@ class SolidFileBrowser extends StatefulWidget {
required this.onDirectoryChanged,
required this.friendlyFolderName,
this.initialPath,
this.folderNameOverrides,
});

@override
Expand Down Expand Up @@ -348,6 +357,7 @@ class SolidFileBrowserState extends State<SolidFileBrowser> {
return SolidFileOperations.getFriendlyFolderName(
currentPath,
_homePath,
widget.folderNameOverrides,
);
}

Expand Down
2 changes: 2 additions & 0 deletions lib/src/widgets/solid_file_browser_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,14 @@ class SolidFileBrowserBuilder {
Function(String fileName, String filePath)? onImportCsv,
required Function(String path) onDirectoryChanged,
SolidFileUploadCallbacks? uploadCallbacks,
Map<String, String>? folderNameOverrides,
}) {
return SolidFileBrowser(
key: browserKey,
browserKey: browserKey,
friendlyFolderName: friendlyFolderName,
initialPath: initialPath,
folderNameOverrides: folderNameOverrides,
onFileSelected: onFileSelected ??
(fileName, filePath) {
debugPrint('File selected: $fileName at $filePath');
Expand Down
Loading