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
24 changes: 24 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,27 @@ fi

echo "$result"
printf "\e[32;1m%s\e[0m\n" 'Finished running dart format.'

# Get the commit message.

COMMIT_MSG=$(git log -1 --pretty=%B)

# 20260123 gjw Check if the commit message starts with 'Bump version'
# If it does then run `make versions` to update the version in any
# files that require updating, based on the version from pubspec.yaml.
# Then add any updated files to the commit. I add specific files
# rather than using `.` just to avoid possibly adding things I was not
# planning to. This way we always update, for example, the snap
# version when we 'Bump version', avoiding missing this step, so the
# installer builds that 'Bump version' triggers will get the correct
# versions.

if [[ $COMMIT_MSG == Bump\ version* ]]; then
printf "\e[33;1m%s\e[0m\n" 'Update versions.'

make versions
git add snap/snapcraft.yaml

printf "\e[32;1m%s\e[0m\n" 'Finished updating versions.'

fi
7 changes: 0 additions & 7 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@
#
# If this script exits with a non-zero status nothing will be pushed.

PVER=$(egrep '^version:' pubspec.yaml | cut -d' ' -f2 | cut -d'+' -f1)

if [ -d snap ]; then
printf "\e[33;1m%s\e[0m\n" 'Update snap version.'
perl -pi -e "s|^version:.*|version: ${PVER}|" snap/snapcraft.yaml;
fi

# Flutter Analyze

printf "\e[33;1m%s\e[0m\n" 'Running flutter analyze.'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
types: [opened, reopened, synchronize]

env:
FLUTTER_VERSION: '3.38.5'
FLUTTER_VERSION: '3.38.9'

jobs:

Expand Down
12 changes: 12 additions & 0 deletions example/lib/app_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import 'package:solidui/solidui.dart';

import 'constants/app.dart';
import 'home.dart';
import 'screens/all_pod_files_page.dart';
import 'screens/sample_page.dart';

final _scaffoldController = SolidScaffoldController();
Expand Down Expand Up @@ -73,6 +74,17 @@ class AppScaffold extends StatelessWidget {
''',
child: SolidFile(),
),
SolidMenuItem(
icon: Icons.storage,
title: 'All POD Files',
tooltip: '''

**All POD Files:** Tap here to browse all folders on your POD
from the root.

''',
child: AllPodFilesPage(),
),
],

// APP BAR.
Expand Down
49 changes: 49 additions & 0 deletions example/lib/screens/all_pod_files_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/// All POD Files page - Displays all folders on the POD for testing.
///
/// Copyright (C) 2026, Software Innovation Institute, ANU.
///
/// Licensed under the MIT License (the "License").
///
/// License: https://choosealicense.com/licenses/mit/.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
///
/// Authors: Tony Chen

library;

import 'package:flutter/material.dart';

import 'package:solidui/solidui.dart';

/// A page that browses all folders and files on the POD from the root.

class AllPodFilesPage extends StatelessWidget {
const AllPodFilesPage({super.key});

@override
Widget build(BuildContext context) {
return const SolidFile(
currentPath: SolidFile.podRoot,
friendlyFolderName: 'All POD Files',
showBackButton: true,
backButtonText: 'Back to POD Root',
);
}
}
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
Loading