From 972947fcee0d2da9e618fd94fd6e2992d13f4d61 Mon Sep 17 00:00:00 2001 From: Tony Chen Date: Fri, 13 Mar 2026 01:26:03 +1100 Subject: [PATCH 1/4] Migrate code from SolidUI --- .github/ISSUE_TEMPLATE/bug_report.md | 59 +++ .github/ISSUE_TEMPLATE/feature_request.md | 32 ++ .github/pull_request_template.md | 54 +++ .github/workflows/ci.yaml | 135 ++++++ .gitignore | 259 +++++++++- CHANGELOG.md | 13 + Makefile | 91 ++++ README.md | 249 +++++++++- analysis_options.yaml | 20 + assets/images/app_icon.png | Bin 0 -> 65972 bytes assets/images/app_image.jpg | Bin 0 -> 342232 bytes lib/app.dart | 54 +++ lib/app_scaffold.dart | 151 ++++++ lib/constants/app.dart | 26 + lib/home.dart | 83 ++++ lib/main.dart | 55 +++ lib/screens/all_pod_files_page.dart | 40 ++ lib/utils/is_desktop.dart | 34 ++ license.dart | 27 ++ pubspec.yaml | 37 ++ support/flutter.mk | 551 ++++++++++++++++++++++ support/git.mk | 132 ++++++ support/loc.sh | 132 ++++++ support/modules.mk | 68 +++ 24 files changed, 2286 insertions(+), 16 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/ci.yaml create mode 100644 CHANGELOG.md create mode 100644 Makefile create mode 100644 analysis_options.yaml create mode 100644 assets/images/app_icon.png create mode 100644 assets/images/app_image.jpg create mode 100644 lib/app.dart create mode 100644 lib/app_scaffold.dart create mode 100644 lib/constants/app.dart create mode 100644 lib/home.dart create mode 100644 lib/main.dart create mode 100644 lib/screens/all_pod_files_page.dart create mode 100644 lib/utils/is_desktop.dart create mode 100644 license.dart create mode 100644 pubspec.yaml create mode 100644 support/flutter.mk create mode 100644 support/git.mk create mode 100644 support/loc.sh create mode 100644 support/modules.mk 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..8f5787c --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,135 @@ +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' + 'assets/**/*.md' + 'assets/**/*.html' + 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..cfb3066 100644 --- a/README.md +++ b/README.md @@ -1 +1,248 @@ -# 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/dev/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 + +
+ Screenshots to be added. +
+ +## 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 + +``` +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 MIT License. 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 0000000000000000000000000000000000000000..bb0378af1a3438f646e042ea49d1473ff2f40400 GIT binary patch literal 65972 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