diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..30db7c8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,59 @@ +--- +name: 'bug report' +about: Create a bug report to help improve FilePod. +title: '' +labels: '' +assignees: '' + +--- + +## Describe the Bug + +[A clear and concise description of the bug.] + +## To Reproduce + +Steps to reproduce the behaviour: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +```bash +$ commands +... +``` + +## Expected Behaviour + +[A clear and concise description of what you expected to happen.] + +## Screenshots + +[If applicable, add screenshots to help explain your problem.] + +## Context + +Where has the issue been observed: + +- [ ] Android +- [ ] Chrome +- [ ] iOS +- [ ] Linux +- [ ] macOS +- [ ] Web +- [ ] Windows + +[Add any other context about the problem here.] + +App Version: +Flutter Version: + +## Closing Criteria + +Checklist for closing the issue: + +- [ ] No errors from `make prep` +- [ ] All tests pass `make qtest` +- [ ] [Issue specific requirements.] diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..541ef1b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,32 @@ +--- +name: 'feature request' +about: Suggest an idea or feature improvement for FilePod. +title: '' +labels: '' +assignees: '' +--- + +## Description + +[A clear and concise description of the problem or new functionality.] + +## Why + +So that as a user I can [clear and concise benefit]. + +## Closing Criteria + +Checklist for closing the issue: + +- [ ] No errors from `make prep` +- [ ] All tests pass `make qtest` +- [ ] [Issue specific requirements.] + +## Alternatives + +[A clear and concise description of any alternative solutions or +features you've considered.] + +## Additional Context + +[Add any other context or screenshots about the feature request here.] diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..4f42ea9 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,54 @@ +# Pull Request Details + +## What issue does this PR address + +- [Description] + +## Associated Issue + +- This PR relates to issue # + +## Type of Change + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## How Has This Been Tested? + +Please describe the tests that you ran to verify your changes. + +## Checklist + +Complete the check-list below to ensure your branch is ready for PR. + +- [ ] Screenshots included in linked issue # +- [ ] Changes adhere to the [style and coding guidelines](https://survivor.togaware.com/gnulinux/flutter-style.html) +- [ ] I have performed a self-review of my code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] Any dependent changes have been merged and published in downstream modules +- [ ] The update contains no confidential information +- [ ] The update has no duplicated content +- [ ] No lint check errors are related to these changes (`make prep` or `flutter analyze lib`) +- [ ] Integration test `dart test` output or screenshot included in issue # +- [ ] I tested the PR on these devices: + - [ ] Android + - [ ] iOS + - [ ] Linux + - [ ] MacOS + - [ ] Windows + - [ ] Web +- [ ] I have identified reviewers +- [ ] The PR has been approved by reviewers + +## Finalising + +Once PR discussion is complete and reviewers have approved: + +- [ ] Merge dev into the this branch +- [ ] Resolve any conflicts +- [ ] Add a one line summary into the CHANGELOG.md +- [ ] Push to the git repository and review +- [ ] Merge the PR into dev diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..c258407 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,133 @@ +name: Lint Checks + +on: + push: + pull_request: + types: [opened, reopened, synchronize] + +env: + FLUTTER_VERSION: '3.38.9' + +jobs: + + analyze: + runs-on: ubuntu-latest + if: github.event.repository.private == false + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + flutter-version: ${{env.FLUTTER_VERSION}} + - run: flutter pub get + - run: flutter analyze --fatal-infos + + format: + runs-on: ubuntu-latest + if: github.event.repository.private == false + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + flutter-version: ${{env.FLUTTER_VERSION}} + - run: flutter pub get + - run: dart format --set-exit-if-changed . + + unused_code: + runs-on: ubuntu-latest + if: github.event.repository.private == false + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + flutter-version: ${{env.FLUTTER_VERSION}} + - run: flutter pub get + - run: dart pub global activate dart_code_metrics + - run: metrics check-unused-code --disable-sunset-warning lib + + unused_files: + runs-on: ubuntu-latest + if: github.event.repository.private == false + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + flutter-version: ${{env.FLUTTER_VERSION}} + - run: flutter pub get + - run: dart pub global activate dart_code_metrics + - run: metrics check-unused-files --disable-sunset-warning lib + + import_order: + runs-on: ubuntu-latest + if: github.event.repository.private == false + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + flutter-version: ${{env.FLUTTER_VERSION}} + - run: flutter pub get + - run: dart pub global activate import_order_lint + - run: import_order --check + + markdown: + runs-on: ubuntu-latest + if: github.event.repository.private == false + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18 + - run: npm install -g markdownlint-cli + - run: markdownlint *.md lib assets + + link_checker: + runs-on: ubuntu-latest + if: github.event.repository.private == false + permissions: + issues: write + steps: + - uses: actions/checkout@v4 + + - name: Link Checker + id: lychee + uses: lycheeverse/lychee-action@v2 + with: + args: + --no-progress + '*.md' + './**/*.dart' + fail: true + + locmax: + runs-on: ubuntu-latest + if: github.event.repository.private == false + steps: + - uses: actions/checkout@v4 + - run: make locmax + + copyright: + runs-on: ubuntu-latest + if: github.event.repository.private == false + steps: + - uses: actions/checkout@v4 + - name: Check copyright headers + run: | + missing_copyright=$(find lib -type f -name '*.dart' \ + ! -name '*.g.dart' \ + ! -name '*.gr.dart' \ + ! -name '*.freezed.dart' \ + ! -name '*.chopper.dart' \ + ! -name '*.part.dart' \ + ! -name '*.config.dart' \ + -exec grep -L "Copyright" {} \;) + if [ -n "$missing_copyright" ]; then + echo "Files missing copyright headers:" + echo "$missing_copyright" + exit 1 + else + echo "All non-generated Dart files have copyright headers" + fi diff --git a/.gitignore b/.gitignore index 3150b40..101fa97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,258 @@ -# See https://www.dartlang.org/guides/libraries/private-files +######################################################################## +# Local ignores +######################################################################## -# Files and directories created by pub +!lib + +*~ +*.bak + +ARCHIVE +ignore +pubspec.yaml.* + +qtest_*.txt + +installers/*.apk +installers/*.exe +installers/*.snap +installers/*.tar.gz +installers/*-linux.zip +installers/*-macos.zip +installers/*-windows.zip + +.Rhistory + +# Puppeteer browser cache used in testing solidui apps + +.local-chrome/ + +######################################################################## +# From https://github.com/flutter/flutter/blob/master/.gitignore +# Combined with the github flutter .gitignore +# Combined with the flutter create .gitignore +######################################################################## + +#----------------------------------------------------------------------- +# Miscellaneous +#----------------------------------------------------------------------- + +*.class +*.lock +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +#----------------------------------------------------------------------- +# IntelliJ related +#----------------------------------------------------------------------- + +*.iml +*.ipr +*.iws +.idea/ + +#----------------------------------------------------------------------- +# Visual Studio Code related +#----------------------------------------------------------------------- + +.vscode/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +#----------------------------------------------------------------------- +# Flutter repo-specific +#----------------------------------------------------------------------- + +/bin/cache/ +/bin/internal/bootstrap.bat +/bin/internal/bootstrap.sh +/bin/mingit/ +/dev/benchmarks/mega_gallery/ +/dev/bots/.recipe_deps +/dev/bots/android_tools/ +/dev/devicelab/ABresults*.json +/dev/docs/doc/ +/dev/docs/flutter.docs.zip +/dev/docs/lib/ +/dev/docs/pubspec.yaml +/dev/integration_tests/**/xcuserdata +/dev/integration_tests/**/Pods +/packages/flutter/coverage/ +version +analysis_benchmark.json + +# packages file containing multi-root paths + +.packages.generated + +#----------------------------------------------------------------------- +# Flutter/Dart/Pub related +#----------------------------------------------------------------------- + +**/doc/api/ .dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +**/generated_plugin_registrant.dart .packages +.pub-cache/ +.pub-preload-cache/ +.pub/ build/ -# If you're building an application, you may want to check-in your pubspec.lock -pubspec.lock +flutter_*.png +linked_*.ds +unlinked.ds +unlinked_spec.ds + +# If you're building an application, you may want to check-in the +# pubspec.lock. Libraries should not include pubspec.lock, per +# https://dart.dev/guides/libraries/private-files#pubspeclock. -# Directory created by dartdoc -# If you don't generate documentation locally you can remove this line. -doc/api/ +/pubspec.lock -# dotenv environment variables file -.env* +#----------------------------------------------------------------------- +# Avoid committing generated Javascript files. +#----------------------------------------------------------------------- -# Avoid committing generated Javascript files: *.dart.js -# Produced by the --dump-info flag. *.info.json -# When generated by dart2js. Don't specify *.js if your -# project includes source files written in JavaScript. *.js *.js_ *.js.deps *.js.map -.flutter-plugins -.flutter-plugins-dependencies +#----------------------------------------------------------------------- +# Android related +#----------------------------------------------------------------------- + +**/android/**/gradle-wrapper.jar +.gradle/ +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java +**/android/key.properties +*.jks + +android/app/.cxx/ + +#----------------------------------------------------------------------- +# iOS/XCode related +#----------------------------------------------------------------------- + +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/.last_build_id +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# ios/macos generated project files +ios/Flutter/ +ios/Runner.xcodeproj/ +ios/Runner.xcworkspace/ +ios/Runner/ +ios/RunnerTests/ + +# Backups of ios/macos project config yml and Podfile +ios/Podfile-* +ios/project-*.yml + +#----------------------------------------------------------------------- +# macOS +#----------------------------------------------------------------------- + +**/Flutter/ephemeral/ +**/Pods/ +**/macos/Flutter/GeneratedPluginRegistrant.swift +**/macos/Flutter/ephemeral +**/xcuserdata/ + +# ios/macos generated project files +macos/Runner.xcodeproj/ +macos/Runner.xcworkspace/ + +# Backups of ios/macos project config yml and Podfile +macos/Podfile-* +macos/project-*.yml + +#----------------------------------------------------------------------- +# Windows +#----------------------------------------------------------------------- + +**/windows/flutter/generated_plugin_registrant.cc +**/windows/flutter/generated_plugin_registrant.h +**/windows/flutter/generated_plugins.cmake + +#----------------------------------------------------------------------- +# Linux +#----------------------------------------------------------------------- + +**/linux/flutter/generated_plugin_registrant.cc +**/linux/flutter/generated_plugin_registrant.h +**/linux/flutter/generated_plugins.cmake +**/linux/runner + +#----------------------------------------------------------------------- +# Coverage +#----------------------------------------------------------------------- + +coverage/ + +#----------------------------------------------------------------------- +# Symbols +#----------------------------------------------------------------------- + +app.*.symbols + +#----------------------------------------------------------------------- +# Exceptions to above rules. +#----------------------------------------------------------------------- + +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +!/dev/ci/**/Gemfile.lock diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7462cad --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## 0.0.1 + +- Initial release of FilePod. +- Solid POD file browser with upload and download functionality. +- Authentication via SolidLogin. +- Responsive navigation with rail and drawer modes. +- Theme switching (light/dark/system). +- Security key management integration. +- All POD Files view for browsing from the root. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c0bfacf --- /dev/null +++ b/Makefile @@ -0,0 +1,91 @@ +######################################################################## +# +# Makefile for FilePod +# +# Copyright (c) Graham.Williams@togaware.com +# +# License: Creative Commons Attribution-ShareAlike 4.0 International. +# +######################################################################## + +# App is often the current directory name. + +APP=$(shell pwd | xargs basename) +VER = $(shell egrep '^version:' pubspec.yaml | cut -d' ' -f2 | cut -d'+' -f1) +DATE=$(shell date +%Y-%m-%d) + +DEST=/var/www/html/$(APP) + +REPO=solidcommunity.au +RLOC=/var/www/html/installers/ +DWLD=https://$(REPO)/installers/ + +######################################################################## +# Supported Makefile modules. + +INC_BASE=support + +INC_DOCKER=skip +INC_MLHUB=skip +INC_WEBCAM=skip + +INC_MODULE=$(INC_BASE)/modules.mk + +ifneq ("$(wildcard $(INC_MODULE))","") + include $(INC_MODULE) +endif + +######################################################################## +# HELP + +define HELP +$(APP): + + ginstall After a github build download bundles and upload to $(REPO) + + local Install to $(HOME)/.local/share/$(APP) + tgz Upload the installer to $(REPO) + apk Upload the installer to $(REPO) + +endef +export HELP + +help:: + @echo "$$HELP" + +######################################################################## +# LOCAL TARGETS + +clean:: + rm -f README.html + +local: tgz + tar zxvf installers/$(APP).tar.gz -C $(HOME)/.local/share/ + +tgz:: + chmod a+r installers/$(APP)*.tar.gz + rsync -avzh installers/$(APP)*.tar.gz $(REPO):/var/www/html/installers/ + ssh $(REPO) chmod -R go+rX /var/www/html/installers/ + ssh $(REPO) chmod go=x /var/www/html/installers/ + +apk:: + rsync -avzh installers/$(APP).apk $(REPO):$(RLOC) + ssh $(REPO) chmod a+r $(RLOC)$(APP).apk + mv -f installers/$(APP)-*.apk installers/ARCHIVE/ + rm -f installers/$(APP).apk + @echo '' + +appbundle:: + rsync -avzh installers/$(APP).aab $(REPO):$(RLOC) + ssh $(REPO) chmod a+r $(RLOC)$(APP).aab + mv -f installers/$(APP)-*.aab installers/ARCHIVE/ + rm -f installers/$(APP).aab + @echo '' + +.PHONY: upload +upload: + (cd installers; make ginstall) + @echo '' + +.PHONY: ginstall +ginstall: upload apk appbundle diff --git a/README.md b/README.md index ef2d165..0f198d0 100644 --- a/README.md +++ b/README.md @@ -1 +1,255 @@ -# filepod \ No newline at end of file + + +# FilePod + +[![Flutter](https://img.shields.io/badge/Flutter-%2302569B.svg?style=for-the-badge&logo=Flutter&logoColor=white)](https://flutter.dev) +[![Dart](https://img.shields.io/badge/dart-%230175C2.svg?style=for-the-badge&logo=dart&logoColor=white)](https://dart.dev) + +[![GitHub License](https://img.shields.io/github/license/anusii/filepod)](https://raw.githubusercontent.com/anusii/filepod/dev/LICENSE) +[![GitHub Version](https://img.shields.io/badge/dynamic/yaml?url=https://raw.githubusercontent.com/anusii/filepod/dev/pubspec.yaml&query=$.version&label=version&logo=github)](https://github.com/anusii/filepod/blob/main/CHANGELOG.md) +[![GitHub Last Updated](https://img.shields.io/github/last-commit/anusii/filepod?label=last%20updated)](https://github.com/anusii/filepod/commits/dev/) +[![GitHub Commit Activity (dev)](https://img.shields.io/github/commit-activity/w/anusii/filepod/dev)](https://github.com/anusii/filepod/commits/dev/) +[![GitHub Issues](https://img.shields.io/github/issues/anusii/filepod)](https://github.com/anusii/filepod/issues) + +A [Solid](https://solidproject.org) file browser application built +with [Flutter](https://flutter.dev) and +[SolidUI](https://github.com/anusii/solidui). FilePod allows users to +browse, upload, and download files stored on their personal online +data store ([POD](https://solidproject.org/about)), providing a +familiar file-management experience for the decentralised web. + +See the [AU Solid Community](https://solidcommunity.au) page for apps +utilising the Solid ecosystem. + +## Table of Contents + +- [Features](#features) +- [Screenshots](#screenshots) +- [Requirements](#requirements) +- [Installation](#installation) +- [Getting Started](#getting-started) +- [Application Structure](#application-structure) +- [Contributing](#contributing) +- [Licence](#licence) + +## Features + +- **Solid POD File Browsing** — Navigate folders and files stored on + your Solid POD with an intuitive, responsive interface. + +- **File Upload** — Upload files from your local device directly to + your POD. + +- **File Download** — Download files from your POD to your local + device. + +- **All POD Files View** — Browse every folder and file on your POD + from the root, giving a complete overview of your stored data. + +- **Solid Authentication** — Secure login against a Solid server + using the `SolidLogin` widget. + +- **Security Key Management** — Manage encryption keys for private + data stored on your POD. + +- **Responsive Navigation** — Automatically switches between a + vertical navigation rail (wide screens) and a collapsible navigation + drawer (narrow screens). + +- **Theme Switching** — Toggle between light, dark, and system theme + modes. + +- **Cross-Platform** — Runs on macOS, Linux, Windows, Android, iOS, + and the Web. + +## Screenshots + +
+ Login page of FilePod +
+ Home page of FilePod +
+ +## Requirements + +- Flutter SDK: `>=3.10.0` +- Dart SDK: `>=3.0.0 <4.0.0` + +### Dependencies + +FilePod relies on the following key packages: + +- [`solidui`](https://github.com/anusii/solidui) — UI components for + Solid applications +- [`solidpod`](https://github.com/anusii/solidpod) — Solid POD + integration +- `shared_preferences` — Local storage for settings +- `markdown_tooltip` — Markdown-enabled tooltips +- `window_manager` — Desktop window management + +## Installation + +### Clone the Repository + +```bash +git clone https://github.com/anusii/filepod.git +cd filepod +``` + +### Install Dependencies + +```bash +flutter pub get +``` + +## Getting Started + +### Running the Application + +```bash +# macOS +flutter run -d macos + +# Linux +flutter run -d linux + +# Windows +flutter run -d windows + +# Web +flutter run -d chrome +``` + +### Using the Makefile + +A `Makefile` is provided for common development tasks: + +```bash +# Run on macOS +make macos + +# Run on Linux +make linux + +# Run code analysis +make analyze + +# Format code +make format + +# Full preparation for a pull request +make prep +``` + +## Application Structure + +```text +lib/ +├── main.dart # Main entry point +├── app.dart # Root App widget with SolidThemeApp and SolidLogin +├── app_scaffold.dart # SolidScaffold configuration (menu, appBar, statusBar) +├── home.dart # Home page widget +├── constants/ +│ └── app.dart # Application-wide constants +├── screens/ +│ └── all_pod_files_page.dart # Browse all POD files from root +└── utils/ + └── is_desktop.dart # Desktop platform detection utility +``` + +### Key Components + +#### `main.dart` + +Application entry point. Initialises Flutter bindings, configures +the window manager for desktop platforms, and launches the `App()` +widget. + +#### `app.dart` + +Root widget implementing `SolidThemeApp` with theme configuration +and `SolidLogin` for Solid server authentication. After login, the +`AppScaffold` is displayed. + +#### `app_scaffold.dart` + +Configures the `SolidScaffold` with: + +- **Menu items** — Home, Files, and All POD Files navigation +- **App bar** — Title, version information, and file browser action +- **Status bar** — Server info, login status, and security key status +- **About dialogue** — Application information and links +- **Theme toggle** — Light/dark/system mode switching +- **Logout** — Secure session termination + +#### `home.dart` + +Welcome page displaying a feature overview and usage guidance. + +#### `screens/all_pod_files_page.dart` + +A dedicated page that browses all folders and files on the POD +from the root, providing a complete view of the user's stored data. + +## Contributing + +We welcome contributions! Please follow these guidelines: + +1. **Fork** the repository and create a feature branch from `dev`. +2. **Follow** the [coding style + guidelines](https://survivor.togaware.com/gnulinux/flutter-style.html). +3. **Run** `make prep` before submitting a pull request. +4. **Submit** a pull request using the provided + [PR template](.github/pull_request_template.md). + +### Reporting Issues + +- **Bug reports**: Use the [bug report + template](.github/ISSUE_TEMPLATE/bug_report.md). +- **Feature requests**: Use the [feature request + template](.github/ISSUE_TEMPLATE/feature_request.md). + +### Development Setup + +```bash +git clone https://github.com/anusii/filepod.git +cd filepod +flutter pub get +flutter run -d macos +``` + +## Licence + +Copyright (C) 2026, Software Innovation Institute, ANU. + +Licensed under the GNU General Public License, Version 3. +See [LICENSE](LICENSE) for details. + +## Authors + +- Graham Williams +- Tony Chen + +For more information about Solid and PODs, visit +[solidproject.org](https://solidproject.org). + +## Additional Information + +The source code can be accessed via the [GitHub +repository](https://github.com/anusii/filepod). You can also file +issues at [GitHub Issues](https://github.com/anusii/filepod/issues). +The authors of the package will respond to issues as best we can. + + +*Time-stamp: * + + + +[comment]: # (Local Variables:) +[comment]: # (time-stamp-line-limit: -8) +[comment]: # (End:) + diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..9880a29 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,20 @@ +# Configure the analyser for static analysis of Dart code. +# +# Invoke the analyser from the command line: +# +# flutter analyze + +include: package:flutter_lints/flutter.yaml + +linter: + rules: + avoid_print: true + prefer_single_quotes: true + require_trailing_commas: true + directives_ordering: false + prefer_const_constructors: true + +analyzer: + exclude: + - ignore/** + - ignore/ diff --git a/assets/images/app_icon.png b/assets/images/app_icon.png new file mode 100644 index 0000000..bb0378a Binary files /dev/null and b/assets/images/app_icon.png differ diff --git a/assets/images/app_image.jpg b/assets/images/app_image.jpg new file mode 100644 index 0000000..ef47d69 Binary files /dev/null and b/assets/images/app_image.jpg differ diff --git a/lib/app.dart b/lib/app.dart new file mode 100644 index 0000000..fedabec --- /dev/null +++ b/lib/app.dart @@ -0,0 +1,54 @@ +/// The primary App widget. +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the GNU General Public License, Version 3 (the "License"); +/// +/// License: https://www.gnu.org/licenses/gpl-3.0.html +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +/// +/// Authors: Tony Chen + +library; + +import 'package:flutter/material.dart'; + +import 'package:solidui/solidui.dart'; + +import 'app_scaffold.dart'; +import 'constants/app.dart'; + +class App extends StatelessWidget { + const App({super.key}); + + @override + Widget build(BuildContext context) { + return SolidThemeApp( + debugShowCheckedModeBanner: false, + title: appTitle, + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: const Color(0xFF007AFF), + ), + useMaterial3: true, + ), + home: const SolidLogin( + image: AssetImage('assets/images/app_image.jpg'), + logo: AssetImage('assets/images/app_icon.png'), + child: appScaffold, + ), + ); + } +} diff --git a/lib/app_scaffold.dart b/lib/app_scaffold.dart new file mode 100644 index 0000000..7ea58ae --- /dev/null +++ b/lib/app_scaffold.dart @@ -0,0 +1,151 @@ +/// The application scaffold configuration. +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the GNU General Public License, Version 3 (the "License"); +/// +/// License: https://www.gnu.org/licenses/gpl-3.0.html +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +/// +/// Authors: Tony Chen + +library; + +import 'package:flutter/material.dart'; + +import 'package:solidui/solidui.dart'; + +import 'constants/app.dart'; +import 'home.dart'; +import 'screens/all_pod_files_page.dart'; + +final _scaffoldController = SolidScaffoldController(); + +const appScaffold = AppScaffold(); + +class AppScaffold extends StatelessWidget { + const AppScaffold({super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return SolidScaffold( + controller: _scaffoldController, + menu: const [ + SolidMenuItem( + icon: Icons.home, + title: 'Home', + tooltip: ''' + + **Home:** Tap here to return to the main page for the app. + + ''', + child: Home(title: appTitle), + ), + SolidMenuItem( + icon: Icons.folder, + title: 'Files', + tooltip: ''' + + **Files:** Tap here to browse the files on your POD. + + ''', + 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(), + ), + ], + appBar: SolidAppBarConfig( + title: appTitle.split(' - ')[0], + versionConfig: SolidVersionConfig( + changelogUrl: 'https://github.com/anusii/filepod/blob/main/' + 'CHANGELOG.md', + showDate: true, + userTextStyle: TextStyle( + color: theme.colorScheme.onSurface, + ), + ), + actions: [ + SolidAppBarAction( + icon: Icons.folder, + onPressed: () => _scaffoldController.navigateToSubpage( + const SolidFile(), + ), + tooltip: 'Files', + ), + ], + ), + statusBar: const SolidStatusBarConfig( + serverInfo: SolidServerInfo(serverUri: SolidConfig.defaultServerUrl), + loginStatus: SolidLoginStatus(), + securityKeyStatus: SolidSecurityKeyStatus(), + ), + aboutConfig: SolidAboutConfig( + applicationName: appTitle.split(' - ')[0], + applicationIcon: Image.asset( + 'assets/images/app_icon.png', + width: 64, + height: 64, + ), + applicationLegalese: ''' + + © 2026 Software Innovation Institute, the Australian National University + + ''', + text: ''' + + FilePod is a Solid file browser application that allows you + to manage files on your personal online data store (POD). + + Key features: + + 📂 Browse and manage files on your Solid POD; + + 📤 Upload files to your POD; + + 📥 Download files from your POD; + + 🔐 Security key management for encrypted data; + + 🎨 Theme switching (light/dark/system); + + 🧭 Responsive navigation (rail ↔ drawer). + + For more information, visit the + [FilePod](https://github.com/anusii/filepod) GitHub repository and our + [Australian Solid Community](https://solidcommunity.au) web site. + + ''', + ), + themeToggle: const SolidThemeToggleConfig( + enabled: true, + showInAppBarActions: true, + ), + hideNavRail: false, + onLogout: (context) => SolidAuthHandler.instance.handleLogout(context), + child: const Home(title: appTitle), + ); + } +} diff --git a/lib/constants/app.dart b/lib/constants/app.dart new file mode 100644 index 0000000..a616c31 --- /dev/null +++ b/lib/constants/app.dart @@ -0,0 +1,26 @@ +/// App-wide constants. +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the GNU General Public License, Version 3 (the "License"); +/// +/// License: https://www.gnu.org/licenses/gpl-3.0.html +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +/// +/// Authors: Tony Chen + +library; + +const String appTitle = 'FilePod - Solid File Browser'; diff --git a/lib/home.dart b/lib/home.dart new file mode 100644 index 0000000..ed9a1e7 --- /dev/null +++ b/lib/home.dart @@ -0,0 +1,83 @@ +/// The application's home page. +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the GNU General Public License, Version 3 (the "License"); +/// +/// License: https://www.gnu.org/licenses/gpl-3.0.html +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +/// +/// Authors: Tony Chen + +library; + +import 'package:flutter/material.dart'; + +class Home extends StatefulWidget { + const Home({super.key, required this.title}); + + final String title; + + @override + State createState() => _HomeState(); +} + +class _HomeState extends State { + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Card( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + Icons.folder_open, + size: 64, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(height: 16), + Text( + widget.title, + style: Theme.of(context).textTheme.headlineMedium, + ), + const SizedBox(height: 24), + Text( + 'Welcome to FilePod!\n\n' + 'FilePod is a Solid file browser that lets you manage ' + 'files on your personal online data store (POD).\n\n' + 'Key features:\n\n' + '• Browse files and folders on your Solid POD\n' + '• Upload files to your POD\n' + '• Download files from your POD\n' + '• View all POD files from the root\n' + '• Security key management for encrypted data\n' + '• Theme switching (light/dark/system)\n' + '• Responsive navigation (rail ↔ drawer)\n\n' + 'Use the navigation menu to explore your POD files!', + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..d1026cd --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,55 @@ +/// FilePod - Solid File Browser Application +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the GNU General Public License, Version 3 (the "License"); +/// +/// License: https://www.gnu.org/licenses/gpl-3.0.html +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +/// +/// Authors: Tony Chen + +library; + +import 'package:flutter/material.dart'; + +import 'package:window_manager/window_manager.dart'; + +import 'app.dart'; +import 'constants/app.dart'; +import 'utils/is_desktop.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + if (isDesktop) { + await windowManager.ensureInitialized(); + + const windowOptions = WindowOptions( + title: appTitle, + minimumSize: Size(500, 800), + backgroundColor: Colors.transparent, + skipTaskbar: false, + titleBarStyle: TitleBarStyle.normal, + ); + + await windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.show(); + await windowManager.focus(); + }); + } + + runApp(const App()); +} diff --git a/lib/screens/all_pod_files_page.dart b/lib/screens/all_pod_files_page.dart new file mode 100644 index 0000000..f31ebd5 --- /dev/null +++ b/lib/screens/all_pod_files_page.dart @@ -0,0 +1,40 @@ +/// All POD Files page - Displays all folders on the POD from the root. +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the GNU General Public License, Version 3 (the "License"); +/// +/// License: https://www.gnu.org/licenses/gpl-3.0.html +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +/// +/// Authors: Tony Chen + +library; + +import 'package:flutter/material.dart'; + +import 'package:solidui/solidui.dart'; + +class AllPodFilesPage extends StatelessWidget { + const AllPodFilesPage({super.key}); + + @override + Widget build(BuildContext context) { + return const SolidFile( + currentPath: SolidFile.podRoot, + friendlyFolderName: 'All POD Files', + ); + } +} diff --git a/lib/utils/is_desktop.dart b/lib/utils/is_desktop.dart new file mode 100644 index 0000000..3b3040f --- /dev/null +++ b/lib/utils/is_desktop.dart @@ -0,0 +1,34 @@ +/// Check if we are running a desktop (and not a browser). +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the GNU General Public License, Version 3 (the "License"); +/// +/// License: https://www.gnu.org/licenses/gpl-3.0.html +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +/// +/// Authors: Tony Chen + +library; + +import 'dart:io' show Platform; + +import 'package:flutter/foundation.dart' show kIsWeb; + +bool get isDesktop { + if (kIsWeb) return false; + + return Platform.isLinux || Platform.isMacOS || Platform.isWindows; +} diff --git a/license.dart b/license.dart new file mode 100644 index 0000000..49506f7 --- /dev/null +++ b/license.dart @@ -0,0 +1,27 @@ +/// FilePod - A Solid File Browser Application. +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the GNU General Public License, Version 3 (the "License"); +/// +/// License: https://www.gnu.org/licenses/gpl-3.0.html +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +/// +/// Authors: AUTHORS + +// Add the library directive as we have doc entries above. We publish the above +// meta doc lines in the docs. + +library; diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..815d169 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,37 @@ +name: filepod +description: >- + FilePod - A Solid File Browser Application for managing files + on your personal online data store (POD). +publish_to: 'none' +version: 0.0.1+1 + +environment: + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" + +dependencies: + flutter: + sdk: flutter + markdown_tooltip: 0.0.10 + shared_preferences: ^2.5.4 + solidpod: ^0.10.1 + solidui: + git: + url: https://github.com/anusii/solidui.git + ref: dev + window_manager: ^0.5.1 + +dependency_overrides: + solidpod: + git: + url: https://github.com/anusii/solidpod.git + ref: dev + +dev_dependencies: + flutter_lints: ^6.0.0 + +flutter: + uses-material-design: true + + assets: + - assets/images/ diff --git a/support/flutter.mk b/support/flutter.mk new file mode 100644 index 0000000..ac54e78 --- /dev/null +++ b/support/flutter.mk @@ -0,0 +1,551 @@ +######################################################################## +# +# Makefile template for Flutter +# +# Copyright 2021-2025 (c) Graham.Williams@togaware.com +# +# License: Creative Commons Attribution-ShareAlike 4.0 International. +# +######################################################################## + +# App version numbers +# Major release +# Minor update +# Trivial update or bug fix + +ifeq ($(VER),) + VER = $(if $(wildcard pubspec.yaml),$(shell egrep '^version:' pubspec.yaml | cut -d' ' -f2),) +endif + +define FLUTTER_HELP +flutter: + + android Run with an attached Android device; + chrome Run with the chrome device; + emu Run with the android emulator; + linux Run with the linux device; + qlinux Run with the linux device and debugPrint() turned off; + macos Run with the macos device; + + prep Prep for PR by running tests, checks, docs. + push Do a git push and bump the build number if there is one. + + docs Run `dart doc` to create documentation. + + import_order Run import order checking. + import_order_fix Run import order fixing. + + pubspec Choose actual/local pubspec using meld. + pubspec.local Overwrite with local pubspec. + pubspec.actual Overwrite with actual pubspec. + + fix Run `dart fix --apply`. + format Run `dart format`. + analyze Run `flutter analyze`. + depend Run `dart run dependency_validator`. + ignore Look for usage of ignore directives. + license Look for missing top license in source code. + + test Run flutter testing. + itest Run flutter interation testing. + qtest Run above test with PAUSE=0. + qtest.all Run qtest with output redirected - good running all tests. + coverage Run with `--coverage`. + coview View the generated html coverage in browser. + + riverpod Setup `pubspec.yaml` to support riverpod. + runner Build the auto generated code as *.g.dart files. + + desktops Set up for all desktop platforms (linux, windows, macos) + + distributions + apk Builds installers/$(APP).apk. + tgz Builds installers/$(APP).tar.gz. + dmg-unsigned Builds unsigned macos app. + dmg-dev Builds macos app with development certificate. + dmg-staging Builds macos app with distribution certificate. + (TODO convert to dmg). + + publish Publish a package to pub.dev + +Also supported: + + *.itest + *.qtest + +endef +export FLUTTER_HELP + +help:: + @echo "$$FLUTTER_HELP" + +TICK=\033[0;32m✔\033[0m +CROSS=\033[31m❌\033[0m + +DART_CODE=lib \ + $(if $(wildcard test/),test) \ + $(if $(wildcard integration_test/),integration_test) + +# Cater for the case where the support folder is one directory up. + +LOC := $(shell if [ -f support/loc.sh ]; then echo support/loc.sh; \ + elif [ -f ../support/loc.sh ]; then echo ../support/loc.sh; fi) + +.PHONY: chrome +chrome: + flutter run -d chrome --release + +# 20220503 gjw The following fails if the target files already exist - +# just needs to be run once. +# +# dart run build_runner build --delete-conflicting-outputs +# +# List the files that are automatically generated. Then they will get +# built as required. + +# BUILD_RUNNER = \ +# lib/models/synchronise_time.g.dart + +# $(BUILD_RUNNER): +# dart run build_runner build --delete-conflicting-outputs + +pubspec.lock: + flutter pub get + +.PHONY: upgrade +upgrade: + flutter pub upgrade + +.PHONY: linux +linux: pubspec.lock $(BUILD_RUNNER) upgrade + flutter run --device-id linux + +# Turn off debugPrint() output. + +.PHONY: qlinux +qlinux: pubspec.lock $(BUILD_RUNNER) upgrade + flutter run --dart-define DEBUG_PRINT="FALSE" --device-id linux + +.PHONY: macos +macos: $(BUILD_RUNNER) upgrade + flutter run --device-id macos + +.PHONY: android +android: $(BUILD_RUNNER) upgrade + flutter run --device-id $(shell flutter devices | grep android | tr '•' '|' | tr -s '|' | tr -s ' ' | cut -d'|' -f2 | tr -d ' ') + +.PHONY: emu +emu: + @if [ -n "$(shell flutter devices | grep emulator | cut -d" " -f 6)" ]; then \ + flutter run --device-id $(shell flutter devices | grep emulator | cut -d" " -f 6); \ + else \ + flutter emulators --launch Pixel_3a_API_30; \ + echo "Emulator has been started. Rerun `make emu` to build the app."; \ + fi + +.PHONY: linux_config +linux_config: + flutter config --enable-linux-desktop + +.PHONY: prep +prep: analyze fix import_order_fix format dcm ignore license todo locgo markdown lychee depend bakfind + @echo "ADVISORY: make test tests docs" + @echo $(SEPARATOR) + +.PHONY: docs +docs:: + dart doc + chmod -R go+rX doc + +SEPARATOR="------------------------------------------------------------------------" + +.PHONY: pubspec +pubspec: + meld pubspec.yaml.actual pubspec.yaml pubspec.yaml.local + +.PHONY: pubspec.local +pubspec.local: + cp --backup pubspec.yaml pubspec.yaml.actual + cp --backup pubspec.yaml.local pubspec.yaml + +.PHONY: pubspec.actual +pubspec.actual: + cp --backup pubspec.yaml.actual pubspec.yaml + +.PHONY: fix +fix: + @echo "Dart: FIX" + dart fix --apply + @echo $(SEPARATOR) + +.PHONY: format +format: + @echo "Dart: FORMAT" + dart format lib/ $(if $(shell test -d example && echo yes),example/) + @echo $(SEPARATOR) + +# My emacs IDE is starting to add imports of backups automagically! + +.PHONY: bakfind +bakfind: + @echo "Find imports of backups.\n" + @-! find lib -type f -name '*.dart' -exec grep '\.dart\.~\([0-9]\)~' {} + + @echo $(SEPARATOR) + +.PHONY: bakfix +bakfix: + @echo "Find and fix imports of backups." + find lib -type f -name '*.dart*' -exec sed -i 's/\.dart\.~\([0-9]\)~/\.dart/g' {} + + @echo $(SEPARATOR) + +.PHONY: tests +tests:: test qtest + +.PHONY: analyze +analyze: + @echo "Futter ANALYZE" + -flutter analyze +# dart run custom_lint + @echo $(SEPARATOR) + +# dart pub global activate dependency_validator + +.PHONY: depend +depend: + @echo "Dart: REVIEW DEPENDENCIES." + -dependency_validator + @echo $(SEPARATOR) + +# Check and fail if any files exceed limit. +# +# 20260115 gjw We utilise two targets both running locbase. The target +# `locgo` ignores failure of the max loc check and is used in the +# `prep` target above to ensure all tests are undertaken. It is +# wrapped in the common echos for the `prep` workflow. The main target +# `locmax` is used in the CI to fail on too many lines of code, and +# thus fails the lint checking. + +LINES ?= 301 + +.PHONY: locmax +locmax: + @loc=$$(bash $(LOC) -t $(shell find lib -name '*.dart')); \ + totl=$$(cat $(shell find lib -name '*.dart') | wc -l); \ + numf=$$(find lib -name "*.dart" -type f | wc -l); \ + output=$$(bash $(LOC) -n $(LINES) $(shell find lib -name '*.dart') | sort -nr); \ + locm=$$(echo $$output | wc -w | awk '{print $$1/2}'); \ + if [ -n "$$output" ]; then \ + echo "$$output"; \ + echo "\nTotal $$loc lines of code across $$numf files with total $$totl lines."; \ + echo "\n$(CROSS) Error: Found $$locm files with more than $(LINES) lines of code."; \ + exit 1; \ + else \ + echo "Total $$loc lines of code across $$numf files with total $$totl lines."; \ + echo "\n$(TICK) All files are under $(LINES) lines."; \ + fi + +.PHONY: locgo +locgo: + @echo "Files with EXCESS LINES OF CODE:\n" + @-make --no-print-directory locmax + @echo $(SEPARATOR) + +# dart pub global activate dependency_validator + +.PHONY: markdown +markdown: + @echo "Markdown: MARKDOWN FORMAT CHECK." + -markdownlint *.md lib assets installers + @echo + @echo $(SEPARATOR) + +.PHONY: ignore +ignore: + @echo "Files that override lint checks with IGNORE:\n" + @-if grep -r -n 'ignore: ' lib; then exit 1; else exit 0; fi + @echo $(SEPARATOR) + +.PHONY: todo +todo: + @echo "Files that include TODO items to be resolved:\n" + @-if grep -r -n ' TODO ' lib; then echo; exit 1; else exit 0; fi + @echo $(SEPARATOR) + +.PHONY: license +license: + @echo "Files without a LICENSE:\n" + @-output=$$(find lib -type f -not -name '*~' -not -name 'README*' -not -name '*.g.dart' \ + ! -exec grep -qE '^(///? Copyright|///? Licensed)' {} \; -print | xargs printf "\t%s\n"); \ + if [ $$(echo "$$output" | wc -w) -ne 0 ]; then \ + echo "$$output"; \ + echo "\n$(CROSS) Error: Files with no license found."; \ + exit 1; \ + else \ + echo "$(TICK) All source files contain a license."; \ + fi + @echo $(SEPARATOR) + +.PHONY: riverpod +riverpod: + flutter pub add flutter_riverpod + flutter pub add riverpod_annotation + flutter pub add dev:riverpod_generator + flutter pub add dev:build_runner + flutter pub add dev:custom_lint + flutter pub add dev:riverpod_lint + +.PHONY: runner +runner: + dart run build_runner build + +# Support desktop platforms: Linux, MacOS and Windows. Using the +# project name as in the already existant pubspec.yaml ensures the +# project name is a valid name. Otherwise it is obtained from the +# folder name and may not necessarily be a valid flutter project name. + +.PHONY: desktops +desktops: + flutter create --platforms=windows,macos,linux --project-name $(shell grep 'name: ' pubspec.yaml | awk '{print $$2}') . + +######################################################################## +# INTEGRATION TESTING +# +# Run the integration tests for the desktop device (linux, windows, +# macos). Without this explictly specified, if I have my android +# device connected to the computer then the testing defaults to trying +# to install on android. 20230713 gjw + +.PHONY: test +test: + @echo "Unit TEST:" + @-if [ -d test ]; then flutter test; else echo "\nNo test folder found."; fi + @echo $(SEPARATOR) + +# For a specific interactive test we think of it as providing a +# demonstration of the app functionality that we may actually use to +# create a narrated video. A INTERACT of 5 or more is then useful. + +%.itest: + @case "$$(uname -s)" in \ + Linux*) device_id="linux" ;; \ + Darwin*) device_id="macos" ;; \ + MINGW*|MSYS*|CYGWIN*) device_id="windows" ;; \ + *) echo "Unsupported platform: $$(uname -s)"; exit 1 ;; \ + esac; \ + flutter test --dart-define=INTERACT=5 --device-id $$device_id integration_test/$*.dart + +# For a run over all tests interactively we INTERACT a little but not as +# much as when running the individual tests. + +.PHONY: itest +itest: + @case "$$(uname -s)" in \ + Linux*) device_id="linux" ;; \ + Darwin*) device_id="macos" ;; \ + MINGW*|MSYS*|CYGWIN*) device_id="windows" ;; \ + *) echo "Unsupported platform: $$(uname -s)"; exit 1 ;; \ + esac; \ + for t in integration_test/*.dart; do flutter test --dart-define=INTERACT=2 --device-id $$device_id $$t; done + @echo $(SEPARATOR) + +# For the quick tests we do not INTERACT at all. The aim is to quickly +# test all functionality. + +.PHONY: qtest +qtest: + @case "$$(uname -s)" in \ + Linux*) device_id="linux" ;; \ + Darwin*) device_id="macos" ;; \ + MINGW*|MSYS*|CYGWIN*) device_id="windows" ;; \ + *) echo "Unsupported platform: $$(uname -s)"; exit 1 ;; \ + esac; \ + if [ ! -d integration_test ]; then echo "No integration tests available."; exit 0; fi; \ + for t in $$(find integration_test -name "*_test.dart" | sort); do \ + echo "========================================"; \ + echo $$t; /bin/echo -n $$t >&2; \ + echo "========================================"; \ + flutter test --dart-define=INTERACT=0 --device-id $$device_id --reporter failures-only $$t 2>/dev/null; \ + if [ "$$?" -eq 0 ]; then /bin/echo ' YES' >&2; else /bin/echo -n ' ...' >&2; \ + echo '****************************************> TRY AGAIN'; \ + flutter test --dart-define=INTERACT=0 --device-id $$device_id --reporter failures-only $$t 2>/dev/null; \ + if [ "$$?" -eq 0 ]; then /bin/echo ' YES' >&2; else /bin/echo ' NO *****' >&2; fi; fi; \ + done + @echo $(SEPARATOR) + +%.qtest: + @case "$$(uname -s)" in \ + Linux*) device_id="linux" ;; \ + Darwin*) device_id="macos" ;; \ + MINGW*|MSYS*|CYGWIN*) device_id="windows" ;; \ + *) echo "Unsupported platform: $$(uname -s)"; exit 1 ;; \ + esac; \ + flutter test --dart-define=INTERACT=0 --device-id $$device_id --reporter failures-only integration_test/$*.dart 2>/dev/null + +.PHONY: qtest.all +qtest.all: + @echo $(APP) `egrep '^version: ' pubspec.yaml` + @echo "flutter version:" `flutter --version | head -1 | cut -d ' ' -f 2` + make qtest > qtest_$(shell date +%Y%m%d%H%M%S).txt + +clean:: + rm -f qtest_*.txt + +.PHONY: atest +atest: + @echo "Full integration TEST:" + flutter test --dart-define=INTERACT=0 --verbose --device-id \ + $(shell flutter devices | grep desktop | perl -pe 's|^[^•]*• ([^ ]*) .*|\1|') \ + integration_test + @echo $(SEPARATOR) + +.PHONY: coverage +coverage: + @echo "COVERAGE" + @flutter test --coverage + @echo + @-/bin/bash support/coverage.sh + @echo $(SEPARATOR) + +.PHONY: coview +coview: + @genhtml coverage/lcov.info -o coverage/html + @open coverage/html/index.html + +realclean:: + rm -rf coverage + +# Crate an installer for Linux as a tar.gz archive. + +tgz:: $(APP)-$(VER)-linux-x86_64.tar.gz + +$(APP)-$(VER)-linux-x86_64.tar.gz: clean + mkdir -p installers + rm -rf build/linux/x64/release + flutter build linux --release + tar --transform 's|^build/linux/x64/release/bundle|$(APP)|' -czvf $@ build/linux/x64/release/bundle + cp $@ installers/ + mv $@ installers/$(APP).tar.gz + +apk:: + @echo '******************** BUILD ANDROID APK' + flutter build apk --release + cp build/app/outputs/flutter-apk/app-release.apk installers/$(APP).apk + cp build/app/outputs/flutter-apk/app-release.apk installers/$(APP)-$(VER).apk + +appbundle:: + @echo '******************** BUILD ANDROID AAB' + flutter clean + flutter build appbundle --release + cp build/app/outputs/bundle/release/app-release.aab installers/$(APP).aab + cp build/app/outputs/bundle/release/app-release.aab installers/$(APP)-$(VER).aab + +realclean:: + flutter clean + flutter pub get + +# Create a macos app +# [20251029 jesscmoore] TODO: add converting to dmg +# Build unsigned macos app +dmg-unsigned:: + flutter clean + flutter build macos --release --flavor unsigned + +# Build macos app signed with development certificate for testing +# by App Developer Program togaware registered devices +dmg-dev:: + flutter clean + flutter build macos --release --flavor dev + +# Build macos app signed with app store distribution for testing +# on Testflight or publishing +dmg-staging: + flutter clean + flutter build macos --release --flavor staging + +# For the `dev` branch only, update the version sequence number prior +# to a push (relies on the git.mk being loaded after this +# flutter.mk). This is only undertaken through `make push` rather than +# a `git push` in any other way. If +# the pubspec.yaml is not using a build number then do not push to bump +# the build number. + +VERSEQ=$(shell grep '^version: ' pubspec.yaml | cut -d'+' -f2 | awk '{print $$1+1}') + +BRANCH := $(shell git branch --show-current) + +ifeq ($(BRANCH),dev) +push:: + @echo $(SEPARATOR) + perl -pi -e 's|(^version: .*)\+.*|$$1+$(VERSEQ)|' pubspec.yaml + -egrep '^version: .*\+.*' pubspec.yaml && \ + git commit -m "Bump sequence $(VERSEQ)" pubspec.yaml +endif + +.PHONY: publish +publish: + dart pub publish + +# dart pub global activate import_order_lint + +.PHONY: import_order +import_order: + @echo "Dart: CHECK IMPORT ORDER" + @which import_order > /dev/null 2>&1 \ + || { echo "Error: Install with 'dart pub global activate import_order_lint'."; exit 1; } + import_order --check $(DART_CODE) + @echo $(SEPARATOR) + +.PHONY: import_order_fix +import_order_fix: + @echo "Dart: FIX IMPORT ORDER" + @import_order --check $(DART_CODE) \ + || import_order lib $(DART_CODE) + @echo $(SEPARATOR) + +# dart pub global activate dart_code_metrics + +.PHONY: dcm +dcm: unused_code unused_files + +.PHONY: unused_code +unused_code: + @echo "Dart Code Metrics: UNUSED CODE" + -metrics check-unused-code --disable-sunset-warning lib + @echo $(SEPARATOR) + +.PHONY: unused_files +unused_files: + @echo "Dart Code Metrics: UNUSED FILES" + -metrics check-unused-files --disable-sunset-warning lib + @echo $(SEPARATOR) + +.PHONY: lychee +lychee: + @echo "Lychee: CHECK LINKS." + -lychee --no-progress --format compact *.md ./**/*.dart $(if $(wildcard ./**/*.md),./**/*.md) $(if $(wildcard ./**/*.html),./**/*.html) + @echo $(SEPARATOR) + +### TODO THESE SHOULD BE CHECKED AND CLEANED UP + +.PHONY: docs +docs:: + rsync -avzh doc/api/ root@solidcommunity.au:/var/www/html/docs/$(APP)/ + +.PHONY: versions +versions: + if [ -d snap ]; then perl -pi -e 's|^version:.*|version: $(VER)|' snap/snapcraft.yaml; fi + +.PHONY: loc +loc: lib/*.dart + @bash $(LOC) $(shell find lib -name '*.dart') | sort -nr + +# +# Manage the production install on the remote server. +# + +.PHONY: solidcommunity +solidcommunity: + rsync -avzh ./ solidcommunity.au:projects/$(APP)/ \ + --exclude .dart_tool --exclude build --exclude ios --exclude macos \ + --exclude linux --exclude windows --exclude android + ssh solidcommunity.au '(cd projects/$(APP); flutter upgrade; make prod)' diff --git a/support/git.mk b/support/git.mk new file mode 100644 index 0000000..6814efb --- /dev/null +++ b/support/git.mk @@ -0,0 +1,132 @@ +######################################################################## +# +# Makefile template for Version Control - git +# +# Time-stamp: +# +# Copyright 2018-2024 (c) Graham.Williams@togaware.com +# +# License: Creative Commons Attribution-ShareAlike 4.0 International. +# +######################################################################## + +define GIT_HELP +git: + + info Identify the git repository; + status Status listing untracked files; + qstatus A quieter status ignoring untracked files; + + enter Do a git status, fetch, and rebase + exit Do a git status + + push + pull + + fetch Update local repo from remote. + stash Stash changes to allow a rebase. + merge Update local repo with remote updates. + rebase Rebase local repo to include remote in history. + pop Pop the stash. + + main Checkout the main branch; + dev Checkout the dev branch; + log + flog Show the full log; + gdiff + vdiff Show a visual diff using meld. + + upstream Merge from upstrem to local repo. + +endef +export GIT_HELP + +help:: + @echo "$$GIT_HELP" + +info: + @echo "-------------------------------------------------------" + git config --get remote.origin.url + @echo "-------------------------------------------------------" + +status: + @echo "-------------------------------------------------------" + git status + @echo "-------------------------------------------------------" + +qstatus: + @echo "-------------------------------------------------------" + git status --untracked-files=no + @echo "-------------------------------------------------------" + +diff: + @echo "-------------------------------------------------------" + git diff + @echo "-------------------------------------------------------" + +enter:: status fetch rebase +exit:: status push + +# Use :: to allow push to be augmented in other makefiles. + +push:: + @echo "-------------------------------------------------------" + git push + @echo "-------------------------------------------------------" + +pull: + @echo "-------------------------------------------------------" + git pull --stat + @echo "-------------------------------------------------------" + +fetch: + @echo "-------------------------------------------------------" + git fetch + @echo "-------------------------------------------------------" + +stash: + @echo "-------------------------------------------------------" + git stash + @echo "-------------------------------------------------------" + +rebase: + @echo "-------------------------------------------------------" + git rebase + @echo "-------------------------------------------------------" + +pop: + @echo "-------------------------------------------------------" + git stash pop + @echo "-------------------------------------------------------" + +main: + @echo "-------------------------------------------------------" + git checkout main + @echo "-------------------------------------------------------" + +dev: + @echo "-------------------------------------------------------" + git checkout $(USER)/dev + @echo "-------------------------------------------------------" + +log: + @echo "-------------------------------------------------------" + git --no-pager log --stat --max-count=10 + @echo "-------------------------------------------------------" + +flog: + @echo "-------------------------------------------------------" + git --no-pager log + @echo "-------------------------------------------------------" + +gdiff: + @echo "-------------------------------------------------------" + git --no-pager diff --color + @echo "-------------------------------------------------------" + +vdiff: + git difftool --tool=meld + +upstream: + git fetch upstream + git merge upstream/main diff --git a/support/loc.sh b/support/loc.sh new file mode 100644 index 0000000..c526c42 --- /dev/null +++ b/support/loc.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +IGNORE=false # Return an error if any files have more than N loc. +MAX=300 # Value of N loc. +CONCATE=false # Whether to simply count all lines across all files. +THRESHOLD=0 +CLEAN=false + +# Function to display help + +show_help() { + echo "Usage: $0 [options] " + echo "" + echo "Options:" + echo " -c, --clean Just filter the file to remove lines not counted." + echo " -i, --ignore Ignore errors related to the line count threshold." + echo " -t, --total Concatenate all supplied files and count total lines after cleansing." + echo " -n, --max-lines Set the maximum number of allowed lines (default: ${MAX})." + echo " -h, --help Show this help message." + exit 0 +} + +# Function to cleanse lines. + +cleanse_lines() { + sed '1d' "$1" | + grep -v '^$' | # Remove empty lines + grep -v '^[[:space:]]*//' | # Remove comment only lines + grep -v '^\(import\|library\)' | # Remove library and import statements + grep -v '^\s*@' | # Remove directives + grep -v '^\s*[\}\)]' | # Remove linest that start with a bracket + grep -v '^\s*\]' | # Needed this as special case + grep -v '^\s*(\?|:) \[' | # Remove lines that consist of '? [' or ': [' + grep -v '\s*\w*: \[' | # Remove parameter list lines like ` names: [` + grep -v "^\s*['][^']*[']" | # Remove lines that are only a string. + cat +} + +wc_cleanse_lines() { + cleanse_lines "$1" | wc -l +} + + + +# Check for no arguments. + +if [ "$#" -eq 0 ]; then + show_help +fi + +# Parse input arguments. + +while [[ "$1" != "" ]]; do + case $1 in + -c | --clean) + CLEAN=true + shift + ;; + -i | --ignore) + IGNORE=true + shift + ;; + -t | --total) + CONCATE=true + shift + ;; + -n | --max-lines) + shift + if [[ "$1" =~ ^[0-9]+$ ]]; then + THRESHOLD=$1 + shift + else + THRESHOLD=${MAX} + fi + ;; + -h | --help) + show_help + ;; + *) + FILES+=("$1") + shift + ;; + esac +done + +if [ "$CLEAN" = true ]; then + for file in "${FILES[@]}"; do + cleanse_lines "$file" + done + exit 0 +fi + +if [ "$CONCATE" = true ]; then + # Concatenate all files and get total line count after cleansing then exit + TOTAL_LINES=0 + for file in "${FILES[@]}"; do + LINES=$(wc_cleanse_lines "$file") + TOTAL_LINES=$((TOTAL_LINES + LINES)) + done + echo "$TOTAL_LINES" + exit 0 +fi + +# Check individual files for line count. + +ERROR=false + +if [ "$THRESHOLD" -gt 0 ]; then + for file in "${FILES[@]}"; do + LINES=$(wc_cleanse_lines "$file") + if [ "$LINES" -gt "$THRESHOLD" ]; then + printf "%4d %s\n" "$LINES" "$file" + ERROR=true + fi + done +else + for file in "${FILES[@]}"; do + LINES=$(wc_cleanse_lines "$file") + printf "%4d %s\n" "$LINES" "$file" + done +fi + +# Set exit status based on error occurrence +if [ "$ERROR" = true ]; then + if [ "$IGNORE" = true ]; then + exit 0 + else + exit 1 + fi +fi + +exit 0 diff --git a/support/modules.mk b/support/modules.mk new file mode 100644 index 0000000..a11b5a5 --- /dev/null +++ b/support/modules.mk @@ -0,0 +1,68 @@ +######################################################################## +# Supported Makefile modules. +# +# Often the support Makefiles will be in the local support folder, or +# else installed in the local user's shares. + +INC_CLEAN ?= $(INC_BASE)/clean.mk +INC_BOOKDOWN ?= $(INC_BASE)/bookdown.mk +INC_R ?= $(INC_BASE)/r.mk +INC_KNITR ?= $(INC_BASE)/knitr.mk +INC_PANDOC ?= $(INC_BASE)/pandoc.mk +INC_GIT ?= $(INC_BASE)/git.mk +INC_AZURE ?= $(INC_BASE)/azure.mk +INC_LATEX ?= $(INC_BASE)/latex.mk +INC_PDF ?= $(INC_BASE)/pdf.mk +INC_DOCKER ?= $(INC_BASE)/docker.mk +INC_FLUTTER ?= $(INC_BASE)/flutter.mk +INC_JEKYLL ?= $(INC_BASE)/jekyll.mk +INC_MLHUB ?= $(INC_BASE)/mlhub.mk +INC_WEBCAM ?= $(INC_BASE)/webcam.mk +INC_INSTALL ?= $(INC_BASE)/install.mk + +ifneq ("$(wildcard $(INC_CLEAN))","") + include $(INC_CLEAN) +endif +ifneq ("$(wildcard $(INC_BOOKDOWN))","") + include $(INC_BOOKDOWN) +endif +ifneq ("$(wildcard $(INC_R))","") + include $(INC_R) +endif +ifneq ("$(wildcard $(INC_KNITR))","") + include $(INC_KNITR) +endif +ifneq ("$(wildcard $(INC_PANDOC))","") + include $(INC_PANDOC) +endif +ifneq ("$(wildcard $(INC_FLUTTER))","") + include $(INC_FLUTTER) +endif +ifneq ("$(wildcard $(INC_GIT))","") + include $(INC_GIT) +endif +ifneq ("$(wildcard $(INC_AZURE))","") + include $(INC_AZURE) +endif +ifneq ("$(wildcard $(INC_LATEX))","") + include $(INC_LATEX) +endif +ifneq ("$(wildcard $(INC_PDF))","") + include $(INC_PDF) +endif +ifneq ("$(wildcard $(INC_DOCKER))","") + include $(INC_DOCKER) +endif +ifneq ("$(wildcard $(INC_JEKYLL))","") + include $(INC_JEKYLL) +endif +ifneq ("$(wildcard $(INC_MLHUB))","") + include $(INC_MLHUB) +endif +ifneq ("$(wildcard $(INC_WEBCAM))","") + include $(INC_WEBCAM) +endif +ifneq ("$(wildcard $(INC_INSTALL))","") + include $(INC_INSTALL) +endif +