diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 0000000..45c2124 --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.38.6" +} \ No newline at end of file diff --git a/.github/actions/flutter-setup/action.yaml b/.github/actions/flutter-setup/action.yaml new file mode 100644 index 0000000..6534571 --- /dev/null +++ b/.github/actions/flutter-setup/action.yaml @@ -0,0 +1,46 @@ +# .github/actions/flutter-setup/action.yml +name: "Flutter Setup Composite Action" +description: "Checks out code, sets up Java/Flutter, caches, and runs pub get" + +# Define inputs for customization (optional, but good practice) +inputs: + flutter-channel: + description: "Flutter channel to use (stable, beta, dev, master)" + required: false + default: "stable" + java-version: + description: "Java version to set up" + required: false + default: "17" + +runs: + using: "composite" # Specify this is a composite action + steps: + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: ${{ inputs.java-version }} + + - name: Set up Flutter SDK + uses: subosito/flutter-action@v2 + with: + channel: ${{ inputs.flutter-channel }} + flutter-version-file: pubspec.yaml + cache: true # Cache Flutter SDK itself + + - name: Cache Flutter dependencies + id: cache-pub + uses: actions/cache@v4 + with: + path: ${{ env.FLUTTER_HOME }}/.pub-cache + key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }} + restore-keys: | + ${{ runner.os }}-flutter-pub- + + - name: Get Flutter dependencies + run: flutter pub get + # Use shell: bash for potential cross-platform compatibility in complex commands + shell: bash + + # Add other common setup steps if needed diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 9ec7edd..3e3ee30 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -40,7 +40,7 @@ template: | **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION exclude-labels: - - "skip-changelog" + - "skip changelog" exclude-contributors: - "Dr-Blank" @@ -55,15 +55,15 @@ autolabeler: branch: - '/feature\/.+/' title: - - "/feat(ure)?/i" + - "/^feat(ure)?/i" body: - "/JIRA-[0-9]{1,4}/" - label: "chore" title: - - "/chore/i" + - "/^chore\b/i" - label: "ui" title: - "/^ui\b/i" - label: "refactor" title: - - "/refactor/i" + - "/^refactor/i" diff --git a/.github/workflows/flutter-ci.yaml b/.github/workflows/flutter-ci.yaml new file mode 100644 index 0000000..c503b65 --- /dev/null +++ b/.github/workflows/flutter-ci.yaml @@ -0,0 +1,218 @@ +name: Flutter CI & Release + +on: + push: + branches: [main] + tags: ["v*.*.*"] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Flutter Environment + uses: ./.github/actions/flutter-setup # Path to the composite action directory + # Pass inputs if needed (optional, using defaults here) + # with: + # flutter-channel: 'stable' + # java-version: '17' + # Debug: Echo current directory contents + - name: List root directory contents + run: | + pwd + ls -la + + # Debug: Recursive directory structure + - name: Show full directory structure + run: | + echo "Full directory structure:" + tree -L 3 + + # Debug: Submodule status and details + - name: Check submodule status + run: | + echo "Submodule status:" + git submodule status + + echo "\nSubmodule details:" + git submodule foreach 'echo $path: && pwd && ls -la' + + # - name: Run static analysis + # run: flutter analyze + + - name: Check formatting + run: | + dart format -o none --set-exit-if-changed lib/ + + - name: Run tests + run: flutter test + + build_android: + name: Build Android APKs + needs: test + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Setup Flutter Environment + uses: ./.github/actions/flutter-setup # Path to the composite action directory + with: + flutter-channel: stable + java-version: 17 + + - name: Accept Android SDK Licenses + run: | + yes | sudo $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses + + - name: Decode android/upload.jks + run: echo "${{ secrets.UPLOAD_KEYSTORE_JKS }}" | base64 --decode > android/upload.jks + + - name: Decode android/key.properties + run: echo "${{ secrets.KEY_PROPERTIES }}" | base64 --decode > android/key.properties + + - name: Build APKs + run: flutter build apk --release --split-per-abi + + - name: Build Universal APK + run: flutter build apk --release + + - name: Rename Universal APK + run: mv build/app/outputs/flutter-apk/{app-release,app-universal-release}.apk + + - name: Build App Bundle + run: flutter build appbundle --release + + - name: Upload Android APK Artifact + uses: actions/upload-artifact@v4 + with: + name: android-release-artifacts + path: | + build/app/outputs/flutter-apk/*-release*.apk + build/app/outputs/bundle/release/*.aab + + build_linux: + needs: test + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Setup Flutter Environment + uses: ./.github/actions/flutter-setup # Path to the composite action directory + + - name: Install Linux dependencies + run: | + sudo apt-get update -y + sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev locate libfuse2 + # Download and install appimagetool + wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage + chmod +x appimagetool-x86_64.AppImage + sudo mv appimagetool-x86_64.AppImage /usr/local/bin/appimagetool + shell: bash + - name: setup fastforge + run: | + dart pub global activate fastforge + - name: Build Linux AppImage and deb + run: fastforge package --platform linux --targets deb,appimage + + - name: Rename Linux Artifacts + run: | + # Find and rename .deb file + DEB_FILE=$(find dist/ -name "*.deb" -type f) + if [ -n "$DEB_FILE" ]; then + mv "$DEB_FILE" dist/vaani-linux-amd64.deb + echo "Renamed DEB: $DEB_FILE to dist/vaani-linux-amd64.deb" + else + echo "Error: .deb file not found in dist/" + exit 1 + fi + + # Find and rename .AppImage file + APPIMAGE_FILE=$(find dist/ -name "*.AppImage" -type f) + if [ -n "$APPIMAGE_FILE" ]; then + mv "$APPIMAGE_FILE" dist/vaani-linux-amd64.AppImage + echo "Renamed AppImage: $APPIMAGE_FILE to dist/vaani-linux-amd64.AppImage" + else + echo "Error: .AppImage file not found in dist/" + exit 1 + fi + shell: bash + + - name: Upload Linux Artifacts + uses: actions/upload-artifact@v4 + with: + name: linux-release-artifacts + path: | + dist/vaani-linux-amd64.deb + dist/vaani-linux-amd64.AppImage + + # Job 4: Create GitHub Release (NEW - runs only on tag pushes) + create_release: + name: Create GitHub Release + needs: [build_android, build_linux] # Depends on successful builds + runs-on: ubuntu-latest + permissions: + contents: write # Need write access to create release + # <<< CONDITION: Only run this job if the trigger was a tag starting with 'v' + if: startsWith(github.ref, 'refs/tags/v') + + steps: + # No checkout needed if only downloading artifacts and using context variables + # - name: Checkout repository + # uses: actions/checkout@v4 + + # Download artifacts created earlier IN THIS SAME WORKFLOW RUN + - name: Download Android Artifacts + uses: actions/download-artifact@v4 + with: + name: android-release-artifacts + path: ./release-artifacts/android + + - name: Download Linux Artifacts + uses: actions/download-artifact@v4 + with: + name: linux-release-artifacts + path: ./release-artifacts/linux + + - name: List downloaded files (for debugging) + run: ls -R ./release-artifacts + shell: bash + + # Extract version info from the tag + - name: Extract Version from Tag + id: version + run: | + TAG_NAME=${GITHUB_REF#refs/tags/} + VERSION=${TAG_NAME#v} + echo "tag=${TAG_NAME}" >> $GITHUB_OUTPUT + echo "version=${VERSION}" >> $GITHUB_OUTPUT + shell: bash + + # Generate release notes (optional, consider its configuration for tags) + - name: Generate Release Notes + id: generate_release_notes + uses: release-drafter/release-drafter@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Create the GitHub Release using downloaded artifacts + - name: Create GitHub Release + uses: ncipollo/release-action@v1 + with: + artifacts: "./release-artifacts/**/*" # Use downloaded artifacts + name: Release v${{ steps.version.outputs.version }} + tag: ${{ github.ref }} + body: ${{ steps.generate_release_notes.outputs.body }} + # token: ${{ secrets.GITHUB_TOKEN }} # Usually inferred diff --git a/.github/workflows/flutter_release.yaml b/.github/workflows/flutter_release.yaml deleted file mode 100644 index e5544be..0000000 --- a/.github/workflows/flutter_release.yaml +++ /dev/null @@ -1,84 +0,0 @@ -name: Flutter Release Workflow - -on: - push: - tags: - - "v**" - # manually trigger a release if needed - workflow_dispatch: - -jobs: - build: - permissions: - # write permission is required to create a github release - contents: write - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Checkout shelfsdk - uses: actions/checkout@v3 - with: - repository: Dr-Blank/shelfsdk - path: ./shelfsdk - - - name: Set Up Java - uses: actions/setup-java@v3.12.0 - with: - distribution: "oracle" - java-version: "17" - - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - - - name: Install dependencies - run: flutter pub get - - # - name: Run tests - # run: flutter test - - - name: Decode android/upload.jks - run: echo "${{ secrets.UPLOAD_KEYSTORE_JKS }}" | base64 --decode > android/upload.jks - - - name: Decode android/key.properties - run: echo "${{ secrets.KEY_PROPERTIES }}" | base64 --decode > android/key.properties - - - name: Build APKs - run: flutter build apk --release --split-per-abi - - - name: Build Universal APK - run: flutter build apk --release - - - name: Rename Universal APK - run: mv build/app/outputs/flutter-apk/{app-release,app-release-universal}.apk - - - name: Build App Bundle - run: flutter build appbundle --release - - - name: version - id: version - run: | - tag=${GITHUB_REF/refs\/tags\//} - version=${tag#v} - major=${version%%.*} - echo "tag=${tag}" >> $GITHUB_OUTPUT - echo "version=${version}" >> $GITHUB_OUTPUT - echo "major=${major}" >> $GITHUB_OUTPUT - - - name: Generate Release Notes - id: generate_release_notes - uses: release-drafter/release-drafter@v6 - env: - GITHUB_TOKEN: ${{ github.token }} - - - name: Create GitHub Release - uses: ncipollo/release-action@v1 - with: - artifacts: "build/app/outputs/flutter-apk/*-release*.apk,build/app/outputs/bundle/release/*.aab" - name: v${{ steps.version.outputs.version }} - tag: ${{ github.ref }} - body: ${{ steps.generate_release_notes.outputs.body }} diff --git a/.github/workflows/flutter_test.yaml b/.github/workflows/flutter_test.yaml deleted file mode 100644 index 9c74813..0000000 --- a/.github/workflows/flutter_test.yaml +++ /dev/null @@ -1,53 +0,0 @@ -name: Flutter Test - -on: - push: - branches: - - main - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Decode android/upload.jks - run: echo "${{ secrets.UPLOAD_KEYSTORE_JKS }}" | base64 --decode > android/upload.jks - - - name: Decode android/key.properties - run: echo "${{ secrets.KEY_PROPERTIES }}" | base64 --decode > android/key.properties - - - name: Checkout shelfsdk - uses: actions/checkout@v3 - with: - repository: Dr-Blank/shelfsdk - path: ./shelfsdk - - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - - - name: Install dependencies - run: flutter pub get - - - name: Run tests - run: flutter test - - - name: Set Up Java - uses: actions/setup-java@v3.12.0 - with: - distribution: "oracle" - java-version: "17" - - - name: Build APK - run: flutter build apk --release - - - name: Upload APKs - uses: actions/upload-artifact@v4 - with: - name: app-release - path: build/app/outputs/flutter-apk/*.apk diff --git a/.github/workflows/prepare-release.yaml b/.github/workflows/prepare-release.yaml new file mode 100644 index 0000000..ceb9105 --- /dev/null +++ b/.github/workflows/prepare-release.yaml @@ -0,0 +1,130 @@ +# .github/workflows/prepare-release.yml +name: Prepare Release (using Cider) + +on: + workflow_dispatch: + inputs: + bump_type: + description: "Type of version bump (patch, minor, major)" + required: true + type: choice + options: + - patch + - minor + - major + default: "patch" + +permissions: + contents: write # NEEDED to commit, push, and tag + +jobs: + bump_version_and_tag: + name: Bump Version and Tag using Cider + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + # Use a PAT if pushing to protected branches is restricted for GITHUB_TOKEN + token: ${{ secrets.PAT_TOKEN }} # Create PAT with repo scope + # token: ${{ secrets.GITHUB_TOKEN }} # this does not trigger other workflows + + # Setup Flutter/Dart environment needed to run dart pub global activate + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" # Or match your project's channel + flutter-version-file: pubspec.yaml + + - name: Install Cider + run: dart pub global activate cider + shell: bash + + # Add pub global bin to PATH for this job + - name: Add pub global bin to PATH + run: echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH + shell: bash + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + shell: bash + + - name: Bump version using Cider + id: bump + run: | + echo "Current version:" + grep '^version:' pubspec.yaml + + # Run cider to bump version and build number + # Cider modifies pubspec.yaml in place + cider bump ${{ github.event.inputs.bump_type }} --bump-build + + echo "New version (after cider bump):" + # Read the *new* version directly from the modified file + new_version_line=$(grep '^version:' pubspec.yaml) + # Extract just the version string (e.g., 1.2.3+4) + new_version=$(echo "$new_version_line" | sed 's/version: *//') + + echo "$new_version_line" + echo "Extracted new version: $new_version" + + if [[ -z "$new_version" ]]; then + echo "Error: Could not extract new version after cider bump." + exit 1 + fi + + # Create tag name (e.g., v1.2.3 - usually tags don't include build number) + # Extract version part before '+' for the tag + version_for_tag=$(echo "$new_version" | cut -d'+' -f1) + new_tag="v$version_for_tag" + echo "New tag: $new_tag" + + # Set outputs for later steps + echo "new_version=$new_version" >> $GITHUB_OUTPUT + echo "new_tag=$new_tag" >> $GITHUB_OUTPUT + shell: bash + + - name: Commit version bump + run: | + # Add pubspec.yaml. Add CHANGELOG.md if cider modifies it and you want to commit it. + git add pubspec.yaml + # git add CHANGELOG.md # Uncomment if needed + + # Check if there are changes to commit + if git diff --staged --quiet; then + echo "No changes detected in pubspec.yaml (or CHANGELOG.md) to commit." + else + # Use the version *without* build number for the commit message usually + git commit -m "chore(release): bump version to ${{ steps.bump.outputs.new_tag }}" + fi + shell: bash + + - name: Create Git tag + # Only run if the commit step actually committed something (check git status) + # or simply run always, it won't hurt if the commit was skipped + run: | + git tag ${{ steps.bump.outputs.new_tag }} + shell: bash + + - name: Push changes and tag + run: | + # Push the commit first (e.g., to main branch - adjust if needed) + # Handle potential conflicts if main changed since checkout? (More advanced setup) + # Check if there are commits to push before pushing branch + if ! git diff --quiet HEAD^ HEAD; then + echo "Pushing commit to main..." + git push origin HEAD:main + else + echo "No new commits to push to main." + fi + + # Always push the tag + echo "Pushing tag ${{ steps.bump.outputs.new_tag }}..." + git push origin ${{ steps.bump.outputs.new_tag }} + shell: bash + + - name: Output New Tag + run: echo "Successfully tagged release ${{ steps.bump.outputs.new_tag }}" diff --git a/.gitignore b/.gitignore index c137f7a..3eee232 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ migrate_working_dir/ .pub-cache/ .pub/ /build/ +dist/ # Symbolication related app.*.symbols @@ -41,6 +42,10 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release +/android/app/.cxx/ -# separate git repo for api sdk -/shelfsdk \ No newline at end of file +# secret keys +/secrets + +# FVM Version Cache +.fvm/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..557497e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "shelfsdk"] + path = shelfsdk + url = https://github.com/Dr-Blank/shelfsdk diff --git a/.vscode/launch.json b/.vscode/launch.json index 18ecea1..3d5d2ff 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,6 +7,7 @@ { "name": "vaani", "request": "launch", + "program": "lib/main.dart", "type": "dart" }, { diff --git a/.vscode/settings.json b/.vscode/settings.json index ffa9f03..fd30314 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,27 +1,35 @@ { - "workbench.colorCustomizations": { - "activityBar.background": "#5A1021", - "titleBar.activeBackground": "#7E162E", - "titleBar.activeForeground": "#FEFBFC" - }, - "files.exclude": { - "**/*.freezed.dart": true, - "**/*.g.dart": true - }, + "cmake.configureOnOpen": false, "cSpell.words": [ "audioplayers", "autolabeler", "Autovalidate", + "Checkmark", + "Debounceable", "deeplinking", "fullscreen", "Lerp", "miniplayer", "mocktail", + "nodename", + "numberpicker", "riverpod", + "Schyler", "shelfsdk", + "sysname", "tapable", "unfocus", + "utsname", "Vaani" ], - "cmake.configureOnOpen": false + "dart.flutterSdkPath": ".fvm/versions/3.38.6", + "files.exclude": { + "**/*.freezed.dart": true, + "**/*.g.dart": true + }, + "workbench.colorCustomizations": { + "activityBar.background": "#5A1021", + "titleBar.activeBackground": "#7E162E", + "titleBar.activeForeground": "#FEFBFC" + } } \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..606e94c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,181 @@ +# Contributing to Vaani + +## Welcome Contributors! 🚀 + +We appreciate your interest in contributing to Vaani. This guide will help you navigate the contribution process effectively. + +## How to Contribute + +### Reporting Bugs 🐞 + +1. **Check Existing Issues**: + - Search through the [GitHub Issues](https://github.com/Dr-Blank/Vaani/issues) + +2. **Create a Detailed Bug Report**: + - Provide: + * Exact steps to reproduce + * Relevant error logs or screenshots + +### Submodule Contribution Workflow 🧩 + +#### Understanding Vaani's Submodule Structure + +Vaani uses Git submodules to manage interconnected components. This means each submodule is a separate Git repository nested within the main project. + +#### Working with Submodules + +1. **Identifying Submodules**: + - List all submodules in the project + ```bash + git submodule status + ``` + +2. **Initializing Submodules**: + ```bash + # Ensure all submodules are initialized + git submodule update --init --recursive + ``` + +3. **Contributing to a Specific Submodule**: + + a. **Navigate to Submodule Directory**: + ```bash + cd path/to/submodule + ``` + + b. **Create a Separate Branch**: + ```bash + git checkout -b feature/your-submodule-feature + ``` + + c. **Make and Commit Changes**: + ```bash + # Stage changes + git add . + + # Commit with descriptive message + git commit -m "feat(submodule): describe specific change" + ``` + + d. **Push Submodule Changes**: + ```bash + git push origin feature/your-submodule-feature + ``` + +4. **Updating Submodule References**: + After making changes to a submodule: + ```bash + # From the main repository root + git add path/to/submodule + git commit -m "Update submodule reference to latest changes" + ``` + +5. **Pulling Latest Submodule Changes**: + ```bash + # Update all submodules + git submodule update --recursive --remote + + # Or update a specific submodule + git submodule update --remote path/to/specific/submodule + ``` + +#### Submodule Contribution Best Practices + +- Always work in a feature branch within the submodule +- Ensure submodule changes do not break the main application +- Write tests for submodule-specific changes +- Update documentation if the submodule's interface changes +- Create a pull request for the submodule first, then update the main project's submodule reference + +### Development Workflow + +#### Setting Up the Development Environment + +1. **Prerequisites**: + - [Git](https://git-scm.com/) + - [Flutter SDK](https://flutter.dev/) + - Recommended IDE: [VS Code](https://code.visualstudio.com/) + +2. **Repository Setup**: + + 1. [Fork the repo](https://github.com/Dr-Blank/Vaani/fork) + 1. Clone the forked repository to your local machine + ```bash + # Fork the main repository on GitHub + git clone --recursive https://github.com/[YOUR_USERNAME]/Vaani.git + cd Vaani + + # Initialize and update submodules + git submodule update --init --recursive + + # Install dependencies for the main app and submodules + flutter pub get + ``` + +#### Coding Standards + +1. **Code Style**: + - Follow [Flutter's style guide](https://dart.dev/guides/language/effective-dart/style) + - Use `dart format` and `flutter analyze` + + ```bash + dart format . + flutter analyze + ``` + +2. **Testing**: + - Write unit and widget tests + - Ensure tests pass for both the main app and submodules + + ```bash + flutter test + ``` + +### Pull Request Process + +1. **Branch Naming**: + - Use descriptive branch names + - Prefix with feature/, bugfix/, or docs/ + + ```bash + git checkout -b feature/add-accessibility-support + ``` + +2. **Commit Messages**: + - Use clear, concise descriptions + - Reference issue numbers when applicable + - Follow conventional commits format: + `(scope): ` + +3. **Pull Request Guidelines**: + - Clearly describe the purpose of your changes + - Include screenshots for visual changes + - Specify if changes affect specific submodules + - Ensure all CI checks pass + +### Signing the app + +once the keystore is created, you can sign the app with the keystore. + +but for github action you need to make a base64 encoded string of the keystore. + +```bash +# convert keystore to base64 +cat android/key.properties | base64 > key.base64 + +# convert keystore to base64 +cat android/upload.jks | base64 > keystore.base64 +``` + +## Communication + +* [Open an Issue](https://github.com/Dr-Blank/Vaani/issues) +* [Discussion Forum](https://github.com/Dr-Blank/Vaani/discussions) + +## Code of Conduct + +* Be respectful and inclusive +* Constructive feedback is welcome +* Collaborate and support fellow contributors + +Happy Contributing! 🌟 \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..7a118b4 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..1db5478 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,221 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.7) + base64 + nkf + rexml + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.3.0) + aws-partitions (1.1018.0) + aws-sdk-core (3.214.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.96.0) + aws-sdk-core (~> 3, >= 3.210.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.176.0) + aws-sdk-core (~> 3, >= 3.210.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.10.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.2.0) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + declarative (0.0.20) + digest-crc (0.6.5) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.112.0) + faraday (1.10.4) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.2) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.1) + faraday (~> 1.0) + fastimage (2.3.1) + fastlane (2.225.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored (~> 1.2) + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + naturally (~> 2.2) + optparse (>= 0.1.1, < 1.0.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.5) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 3) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.54.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.3) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.7.1) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.4.0) + google-cloud-storage (1.47.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.31.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.8) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.6.2) + json (2.9.0) + jwt (2.9.3) + base64 + mini_magick (4.13.2) + mini_mime (1.1.5) + multi_json (1.15.0) + multipart-post (2.4.1) + nanaimo (0.4.0) + naturally (2.2.1) + nkf (0.2.0) + optparse (0.6.0) + os (1.1.4) + plist (3.7.1) + public_suffix (6.0.1) + rake (13.2.1) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.3.9) + rouge (2.0.7) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + security (0.1.5) + signet (0.19.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + sysrandom (1.0.5) + terminal-notifier (2.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unicode-display_width (2.6.0) + word_wrap (1.0.0) + xcodeproj (1.27.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + x64-mingw-ucrt + +DEPENDENCIES + fastlane + +BUNDLED WITH + 2.5.23 diff --git a/README.md b/README.md index c50a48f..14f16fb 100644 --- a/README.md +++ b/README.md @@ -18,19 +18,25 @@ Client for [Audiobookshelf](https://github.com/advplyr/audiobookshelf) server ma ### Android -[Get it on GitHub](https://github.com/Dr-Blank/Vaani/releases/latest/download/app-release-universal.apk) [Get it on Obtainium](http://apps.obtainium.imranr.dev/redirect.html?r=obtainium://add/https://github.com/Dr-Blank/Vaani) +[Get it on Obtainium](http://apps.obtainium.imranr.dev/redirect.html?r=obtainium://add/https://github.com/Dr-Blank/Vaani) +[Get it on Google Play](https://play.google.com/store/apps/details?id=dr.blank.vaani) +[Get it on IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/dr.blank.vaani) +[Get it on GitHub](https://github.com/Dr-Blank/Vaani/releases/latest/download/app-universal-release.apk) -Playstore App is in closed testing. To join testing -1. [Join the Google Group](https://groups.google.com/g/vaani-app) -2. [Join on Android](https://play.google.com/store/apps/details?id=dr.blank.vaani) Or [Join on Web](https://play.google.com/apps/testing/dr.blank.vaani) +*Play Store version is paid if you want to support the development.* + +### Linux + +[Download Linux (.deb)](https://github.com/Dr-Blank/Vaani/releases/latest/download/vaani-linux-amd64.deb) +[Download Linux (AppImage)](https://github.com/Dr-Blank/Vaani/releases/latest/download/vaani-linux-amd64.AppImage) ## Screencaps https://github.com/user-attachments/assets/2ac9ace2-4a3c-40fc-adde-55914e4cf62d -|||| -|:---:|:---:|:---:| -|Home|Book View|Player| +| | | | +| :-----------------------------------------------------------: | :---------------------------------------------------------------: | :-------------------------------------------------------------: | +| Home | Book View | Player | Currently, the app is in development and is not ready for production use. diff --git a/analysis_options.yaml b/analysis_options.yaml index 2c5752a..1a27822 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -25,6 +25,10 @@ linter: # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule require_trailing_commas: true analyzer: + exclude: + - '**.freezed.dart' + - '**.g.dart' + - '**.gr.dart' errors: invalid_annotation_target: ignore plugins: diff --git a/android/app/build.gradle b/android/app/build.gradle index b13356f..49b2a91 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,6 +32,10 @@ android { namespace "dr.blank.vaani" compileSdk flutter.compileSdkVersion ndkVersion flutter.ndkVersion + // The NDK version is set to a specific version since it was not building + // TODO remove when https://github.com/flutter/flutter/issues/139427 is closed + // ndkVersion = "29.0.13113456" + compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -46,12 +50,21 @@ android { main.java.srcDirs += 'src/main/kotlin' } + // see: https://gitlab.com/IzzyOnDroid/repo/-/issues/623#note_2149548690 + // https://android.izzysoft.de/articles/named/iod-scan-apkchecks#blobs + dependenciesInfo { + // Disables dependency metadata when building APKs. + includeInApk = false + // Disables dependency metadata when building Android App Bundles. + includeInBundle = false + } + defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "dr.blank.vaani" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion 23 + minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -80,3 +93,11 @@ flutter { } dependencies {} + +// https://stackoverflow.com/questions/78626580/how-to-resolve-app-execution-failure-due-to-androidx-corecore1-15-0-alpha +configurations.all { + resolutionStrategy { + force "androidx.core:core:1.13.1" + force "androidx.core:core-ktx:1.13.1" + } +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cb0a2a5..3856060 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,9 +7,13 @@ + + Vaani is a client for your (self-hosted) Audiobookshelf server. + +Features: + +- Functional Player: Speed Control, Sleep Timer, Shake to Control Player +- Save data with Offline listening and caching +- Material Design +- Extensive Settings to customize the every tiny detail + +Note: you need an Audiobookshelf server setup for this app to work. Please see https://www.audiobookshelf.org/ on how to setup one if not already. \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/images/featureGraphic.png b/fastlane/metadata/android/en-US/images/featureGraphic.png new file mode 100644 index 0000000..ed9c09d Binary files /dev/null and b/fastlane/metadata/android/en-US/images/featureGraphic.png differ diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png new file mode 100644 index 0000000..b260956 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/icon.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.jpeg b/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.jpeg new file mode 100644 index 0000000..a84ddb6 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.jpeg differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.jpeg b/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.jpeg new file mode 100644 index 0000000..7b6c86b Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.jpeg differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.jpeg b/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.jpeg new file mode 100644 index 0000000..3c1dbeb Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.jpeg differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.jpeg b/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.jpeg new file mode 100644 index 0000000..afce5bb Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.jpeg differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.jpeg b/fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.jpeg new file mode 100644 index 0000000..4a51186 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.jpeg differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/6_en-US.jpeg b/fastlane/metadata/android/en-US/images/phoneScreenshots/6_en-US.jpeg new file mode 100644 index 0000000..e45f00a Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/6_en-US.jpeg differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/7_en-US.jpeg b/fastlane/metadata/android/en-US/images/phoneScreenshots/7_en-US.jpeg new file mode 100644 index 0000000..3e625ed Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/7_en-US.jpeg differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/8_en-US.jpeg b/fastlane/metadata/android/en-US/images/phoneScreenshots/8_en-US.jpeg new file mode 100644 index 0000000..3714195 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/8_en-US.jpeg differ diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt new file mode 100644 index 0000000..0d9933e --- /dev/null +++ b/fastlane/metadata/android/en-US/short_description.txt @@ -0,0 +1 @@ +Beautiful, Fast and Functional Audiobook Player for your Audiobookshelf server. \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/title.txt b/fastlane/metadata/android/en-US/title.txt new file mode 100644 index 0000000..ed0326a --- /dev/null +++ b/fastlane/metadata/android/en-US/title.txt @@ -0,0 +1 @@ +Vaani \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/video.txt b/fastlane/metadata/android/en-US/video.txt new file mode 100644 index 0000000..e69de29 diff --git a/fastlane/report.xml b/fastlane/report.xml new file mode 100644 index 0000000..9f24822 --- /dev/null +++ b/fastlane/report.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/images/vaani_logo.svg b/images/vaani_logo.svg new file mode 100644 index 0000000..6c79c5a --- /dev/null +++ b/images/vaani_logo.svg @@ -0,0 +1,36 @@ + + + + + Created by potrace 1.16, written by Peter Selinger 2001-2019 + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/api/api_provider.dart b/lib/api/api_provider.dart index daffff8..8994644 100644 --- a/lib/api/api_provider.dart +++ b/lib/api/api_provider.dart @@ -2,20 +2,22 @@ import 'dart:convert'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:http/http.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:vaani/db/cache_manager.dart'; +import 'package:vaani/models/error_response.dart'; import 'package:vaani/settings/api_settings_provider.dart'; +import 'package:vaani/settings/models/authenticated_user.dart'; +import 'package:vaani/shared/extensions/obfuscation.dart'; part 'api_provider.g.dart'; // TODO: workaround for https://github.com/rrousselGit/riverpod/issues/3718 -typedef ResponseErrorHandler = void Function( - Response response, [ - Object? error, -]); +typedef ResponseErrorHandler = + void Function(Response response, [Object? error]); final _logger = Logger('api_provider'); @@ -31,23 +33,21 @@ Uri makeBaseUrl(String address) { /// get the api instance for the given base url @riverpod -AudiobookshelfApi audiobookshelfApi(AudiobookshelfApiRef ref, Uri? baseUrl) { +AudiobookshelfApi audiobookshelfApi(Ref ref, Uri? baseUrl) { // try to get the base url from app settings final apiSettings = ref.watch(apiSettingsProvider); baseUrl ??= apiSettings.activeServer?.serverUrl; - return AudiobookshelfApi( - baseUrl: makeBaseUrl(baseUrl.toString()), - ); + return AudiobookshelfApi(baseUrl: makeBaseUrl(baseUrl.toString())); } /// get the api instance for the authenticated user /// /// if the user is not authenticated throw an error @Riverpod(keepAlive: true) -AudiobookshelfApi authenticatedApi(AuthenticatedApiRef ref) { - final apiSettings = ref.watch(apiSettingsProvider); - final user = apiSettings.activeUser; +AudiobookshelfApi authenticatedApi(Ref ref) { + final user = ref.watch(apiSettingsProvider.select((s) => s.activeUser)); if (user == null) { + _logger.severe('No active user can not provide authenticated api'); throw StateError('No active user'); } return AudiobookshelfApi( @@ -58,15 +58,15 @@ AudiobookshelfApi authenticatedApi(AuthenticatedApiRef ref) { /// ping the server to check if it is reachable @riverpod -FutureOr isServerAlive(IsServerAliveRef ref, String address) async { +FutureOr isServerAlive(Ref ref, String address) async { if (address.isEmpty) { return false; } try { - return await AudiobookshelfApi(baseUrl: makeBaseUrl(address)) - .server - .ping() ?? + return await AudiobookshelfApi( + baseUrl: makeBaseUrl(address), + ).server.ping() ?? false; } catch (e) { return false; @@ -76,14 +76,15 @@ FutureOr isServerAlive(IsServerAliveRef ref, String address) async { /// fetch status of server @riverpod FutureOr serverStatus( - ServerStatusRef ref, + Ref ref, Uri baseUrl, [ ResponseErrorHandler? responseErrorHandler, ]) async { - _logger.fine('fetching server status: $baseUrl'); + _logger.fine('fetching server status: ${baseUrl.obfuscate()}'); final api = ref.watch(audiobookshelfApiProvider(baseUrl)); - final res = - await api.server.status(responseErrorHandler: responseErrorHandler); + final res = await api.server.status( + responseErrorHandler: responseErrorHandler, + ); _logger.fine('server status: $res'); return res; } @@ -96,20 +97,32 @@ class PersonalizedView extends _$PersonalizedView { final api = ref.watch(authenticatedApiProvider); final apiSettings = ref.watch(apiSettingsProvider); final user = apiSettings.activeUser; + if (user == null) { + _logger.warning('no active user'); + yield []; + return; + } if (apiSettings.activeLibraryId == null) { // set it to default user library by logging in and getting the library id - final login = - await api.login(username: user!.username!, password: user.password!); - ref.read(apiSettingsProvider.notifier).updateState( - apiSettings.copyWith(activeLibraryId: login!.userDefaultLibraryId), + final login = await ref.read(loginProvider().future); + if (login == null) { + _logger.shout('failed to login, not building personalized view'); + yield []; + return; + } + ref + .read(apiSettingsProvider.notifier) + .updateState( + apiSettings.copyWith(activeLibraryId: login.userDefaultLibraryId), ); + yield []; + return; } // try to find in cache // final cacheKey = 'personalizedView:${apiSettings.activeLibraryId}'; - var key = 'personalizedView:${apiSettings.activeLibraryId! + user!.id!}'; - final cachedRes = await apiResponseCacheManager.getFileFromMemory( - key, - ) ?? + final key = 'personalizedView:${apiSettings.activeLibraryId! + user.id}'; + final cachedRes = + await apiResponseCacheManager.getFileFromMemory(key) ?? await apiResponseCacheManager.getFileFromCache(key); if (cachedRes != null) { _logger.fine('reading from cache: $cachedRes for key: $key'); @@ -126,10 +139,11 @@ class PersonalizedView extends _$PersonalizedView { } } - // ! exagerated delay + // ! exaggerated delay // await Future.delayed(const Duration(seconds: 2)); - final res = await api.libraries - .getPersonalized(libraryId: apiSettings.activeLibraryId!); + final res = await api.libraries.getPersonalized( + libraryId: apiSettings.activeLibraryId!, + ); // debugPrint('personalizedView: ${res!.map((e) => e).toSet()}'); // save to cache if (res != null) { @@ -145,21 +159,19 @@ class PersonalizedView extends _$PersonalizedView { _logger.warning('failed to fetch personalized view'); yield []; } - } // method to force refresh the view and ignore the cache Future forceRefresh() async { // clear the cache + // TODO: find a better way to clear the cache for only personalized view key return apiResponseCacheManager.emptyCache(); } } /// fetch continue listening audiobooks @riverpod -FutureOr fetchContinueListening( - FetchContinueListeningRef ref, -) async { +FutureOr fetchContinueListening(Ref ref) async { final api = ref.watch(authenticatedApiProvider); final res = await api.me.getSessions(); // debugPrint( @@ -169,10 +181,46 @@ FutureOr fetchContinueListening( } @riverpod -FutureOr me( - MeRef ref, -) async { +FutureOr me(Ref ref) async { final api = ref.watch(authenticatedApiProvider); - final res = await api.me.getUser(); - return res!; + final errorResponseHandler = ErrorResponseHandler(); + final res = await api.me.getUser( + responseErrorHandler: errorResponseHandler.storeError, + ); + if (res == null) { + _logger.severe( + 'me failed, got response: ${errorResponseHandler.response.obfuscate()}', + ); + throw StateError('me failed'); + } + return res; +} + +@riverpod +FutureOr login(Ref ref, {AuthenticatedUser? user}) async { + if (user == null) { + // try to get the user from settings + final apiSettings = ref.watch(apiSettingsProvider); + user = apiSettings.activeUser; + if (user == null) { + _logger.severe('no active user to login'); + return null; + } + _logger.fine('no user provided, using active user: ${user.obfuscate()}'); + } + final api = ref.watch(audiobookshelfApiProvider(user.server.serverUrl)); + api.token = user.authToken; + var errorResponseHandler = ErrorResponseHandler(); + _logger.fine('logging in with authenticated api'); + final res = await api.misc.authorize( + responseErrorHandler: errorResponseHandler.storeError, + ); + if (res == null) { + _logger.severe( + 'login failed, got response: ${errorResponseHandler.response.obfuscate()}', + ); + return null; + } + _logger.fine('login response: ${res.obfuscate()}'); + return res; } diff --git a/lib/api/api_provider.g.dart b/lib/api/api_provider.g.dart index 67e86e2..dabe437 100644 --- a/lib/api/api_provider.g.dart +++ b/lib/api/api_provider.g.dart @@ -6,538 +6,536 @@ part of 'api_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$audiobookshelfApiHash() => r'2c310ea77fea9918ccf96180a92075acd037bd95'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning /// get the api instance for the given base url -/// -/// Copied from [audiobookshelfApi]. + @ProviderFor(audiobookshelfApi) -const audiobookshelfApiProvider = AudiobookshelfApiFamily(); +final audiobookshelfApiProvider = AudiobookshelfApiFamily._(); /// get the api instance for the given base url -/// -/// Copied from [audiobookshelfApi]. -class AudiobookshelfApiFamily extends Family { - /// get the api instance for the given base url - /// - /// Copied from [audiobookshelfApi]. - const AudiobookshelfApiFamily(); +final class AudiobookshelfApiProvider + extends + $FunctionalProvider< + AudiobookshelfApi, + AudiobookshelfApi, + AudiobookshelfApi + > + with $Provider { /// get the api instance for the given base url - /// - /// Copied from [audiobookshelfApi]. - AudiobookshelfApiProvider call( - Uri? baseUrl, - ) { - return AudiobookshelfApiProvider( - baseUrl, - ); + AudiobookshelfApiProvider._({ + required AudiobookshelfApiFamily super.from, + required Uri? super.argument, + }) : super( + retry: null, + name: r'audiobookshelfApiProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$audiobookshelfApiHash(); + + @override + String toString() { + return r'audiobookshelfApiProvider' + '' + '($argument)'; } + @$internal @override - AudiobookshelfApiProvider getProviderOverride( - covariant AudiobookshelfApiProvider provider, - ) { - return call( - provider.baseUrl, - ); + $ProviderElement $createElement( + $ProviderPointer pointer, + ) => $ProviderElement(pointer); + + @override + AudiobookshelfApi create(Ref ref) { + final argument = this.argument as Uri?; + return audiobookshelfApi(ref, argument); } - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'audiobookshelfApiProvider'; -} - -/// get the api instance for the given base url -/// -/// Copied from [audiobookshelfApi]. -class AudiobookshelfApiProvider extends AutoDisposeProvider { - /// get the api instance for the given base url - /// - /// Copied from [audiobookshelfApi]. - AudiobookshelfApiProvider( - Uri? baseUrl, - ) : this._internal( - (ref) => audiobookshelfApi( - ref as AudiobookshelfApiRef, - baseUrl, - ), - from: audiobookshelfApiProvider, - name: r'audiobookshelfApiProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$audiobookshelfApiHash, - dependencies: AudiobookshelfApiFamily._dependencies, - allTransitiveDependencies: - AudiobookshelfApiFamily._allTransitiveDependencies, - baseUrl: baseUrl, - ); - - AudiobookshelfApiProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.baseUrl, - }) : super.internal(); - - final Uri? baseUrl; - - @override - Override overrideWith( - AudiobookshelfApi Function(AudiobookshelfApiRef provider) create, - ) { - return ProviderOverride( + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AudiobookshelfApi value) { + return $ProviderOverride( origin: this, - override: AudiobookshelfApiProvider._internal( - (ref) => create(ref as AudiobookshelfApiRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - baseUrl: baseUrl, - ), + providerOverride: $SyncValueProvider(value), ); } - @override - AutoDisposeProviderElement createElement() { - return _AudiobookshelfApiProviderElement(this); - } - @override bool operator ==(Object other) { - return other is AudiobookshelfApiProvider && other.baseUrl == baseUrl; + return other is AudiobookshelfApiProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, baseUrl.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -mixin AudiobookshelfApiRef on AutoDisposeProviderRef { - /// The parameter `baseUrl` of this provider. - Uri? get baseUrl; -} +String _$audiobookshelfApiHash() => r'f23a06c404e11867a7f796877eaca99b8ff25458'; -class _AudiobookshelfApiProviderElement - extends AutoDisposeProviderElement - with AudiobookshelfApiRef { - _AudiobookshelfApiProviderElement(super.provider); +/// get the api instance for the given base url + +final class AudiobookshelfApiFamily extends $Family + with $FunctionalFamilyOverride { + AudiobookshelfApiFamily._() + : super( + retry: null, + name: r'audiobookshelfApiProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + /// get the api instance for the given base url + + AudiobookshelfApiProvider call(Uri? baseUrl) => + AudiobookshelfApiProvider._(argument: baseUrl, from: this); @override - Uri? get baseUrl => (origin as AudiobookshelfApiProvider).baseUrl; + String toString() => r'audiobookshelfApiProvider'; } -String _$authenticatedApiHash() => r'f555efb6eede590b5a8d60cad2e6bfc2847e2d14'; - /// get the api instance for the authenticated user /// /// if the user is not authenticated throw an error -/// -/// Copied from [authenticatedApi]. + @ProviderFor(authenticatedApi) -final authenticatedApiProvider = Provider.internal( - authenticatedApi, - name: r'authenticatedApiProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$authenticatedApiHash, - dependencies: null, - allTransitiveDependencies: null, -); +final authenticatedApiProvider = AuthenticatedApiProvider._(); -typedef AuthenticatedApiRef = ProviderRef; -String _$isServerAliveHash() => r'6ff90b6e0febd2cd4a4d3a5209a59afc778cd3b6'; - -/// ping the server to check if it is reachable +/// get the api instance for the authenticated user /// -/// Copied from [isServerAlive]. -@ProviderFor(isServerAlive) -const isServerAliveProvider = IsServerAliveFamily(); +/// if the user is not authenticated throw an error -/// ping the server to check if it is reachable -/// -/// Copied from [isServerAlive]. -class IsServerAliveFamily extends Family> { - /// ping the server to check if it is reachable +final class AuthenticatedApiProvider + extends + $FunctionalProvider< + AudiobookshelfApi, + AudiobookshelfApi, + AudiobookshelfApi + > + with $Provider { + /// get the api instance for the authenticated user /// - /// Copied from [isServerAlive]. - const IsServerAliveFamily(); + /// if the user is not authenticated throw an error + AuthenticatedApiProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'authenticatedApiProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); - /// ping the server to check if it is reachable - /// - /// Copied from [isServerAlive]. - IsServerAliveProvider call( - String address, - ) { - return IsServerAliveProvider( - address, - ); + @override + String debugGetCreateSourceHash() => _$authenticatedApiHash(); + + @$internal + @override + $ProviderElement $createElement( + $ProviderPointer pointer, + ) => $ProviderElement(pointer); + + @override + AudiobookshelfApi create(Ref ref) { + return authenticatedApi(ref); } - @override - IsServerAliveProvider getProviderOverride( - covariant IsServerAliveProvider provider, - ) { - return call( - provider.address, + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AudiobookshelfApi value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), ); } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'isServerAliveProvider'; } +String _$authenticatedApiHash() => r'284be2c39823c20fb70035a136c430862c28fa27'; + /// ping the server to check if it is reachable -/// -/// Copied from [isServerAlive]. -class IsServerAliveProvider extends AutoDisposeFutureProvider { + +@ProviderFor(isServerAlive) +final isServerAliveProvider = IsServerAliveFamily._(); + +/// ping the server to check if it is reachable + +final class IsServerAliveProvider + extends $FunctionalProvider, bool, FutureOr> + with $FutureModifier, $FutureProvider { /// ping the server to check if it is reachable - /// - /// Copied from [isServerAlive]. - IsServerAliveProvider( - String address, - ) : this._internal( - (ref) => isServerAlive( - ref as IsServerAliveRef, - address, - ), - from: isServerAliveProvider, - name: r'isServerAliveProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$isServerAliveHash, - dependencies: IsServerAliveFamily._dependencies, - allTransitiveDependencies: - IsServerAliveFamily._allTransitiveDependencies, - address: address, - ); - - IsServerAliveProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.address, - }) : super.internal(); - - final String address; + IsServerAliveProvider._({ + required IsServerAliveFamily super.from, + required String super.argument, + }) : super( + retry: null, + name: r'isServerAliveProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); @override - Override overrideWith( - FutureOr Function(IsServerAliveRef provider) create, - ) { - return ProviderOverride( - origin: this, - override: IsServerAliveProvider._internal( - (ref) => create(ref as IsServerAliveRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - address: address, - ), - ); + String debugGetCreateSourceHash() => _$isServerAliveHash(); + + @override + String toString() { + return r'isServerAliveProvider' + '' + '($argument)'; } + @$internal @override - AutoDisposeFutureProviderElement createElement() { - return _IsServerAliveProviderElement(this); + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + final argument = this.argument as String; + return isServerAlive(ref, argument); } @override bool operator ==(Object other) { - return other is IsServerAliveProvider && other.address == address; + return other is IsServerAliveProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, address.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -mixin IsServerAliveRef on AutoDisposeFutureProviderRef { - /// The parameter `address` of this provider. - String get address; -} +String _$isServerAliveHash() => r'bb3a53cae1eb64b8760a56864feed47b7a3f1c29'; -class _IsServerAliveProviderElement - extends AutoDisposeFutureProviderElement with IsServerAliveRef { - _IsServerAliveProviderElement(super.provider); +/// ping the server to check if it is reachable + +final class IsServerAliveFamily extends $Family + with $FunctionalFamilyOverride, String> { + IsServerAliveFamily._() + : super( + retry: null, + name: r'isServerAliveProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + /// ping the server to check if it is reachable + + IsServerAliveProvider call(String address) => + IsServerAliveProvider._(argument: address, from: this); @override - String get address => (origin as IsServerAliveProvider).address; + String toString() => r'isServerAliveProvider'; } -String _$serverStatusHash() => r'2739906a1862d09b098588ebd16749a09032ee99'; - /// fetch status of server -/// -/// Copied from [serverStatus]. + @ProviderFor(serverStatus) -const serverStatusProvider = ServerStatusFamily(); +final serverStatusProvider = ServerStatusFamily._(); /// fetch status of server -/// -/// Copied from [serverStatus]. -class ServerStatusFamily extends Family> { + +final class ServerStatusProvider + extends + $FunctionalProvider< + AsyncValue, + ServerStatusResponse?, + FutureOr + > + with + $FutureModifier, + $FutureProvider { /// fetch status of server - /// - /// Copied from [serverStatus]. - const ServerStatusFamily(); + ServerStatusProvider._({ + required ServerStatusFamily super.from, + required (Uri, ResponseErrorHandler?) super.argument, + }) : super( + retry: null, + name: r'serverStatusProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$serverStatusHash(); + + @override + String toString() { + return r'serverStatusProvider' + '' + '$argument'; + } + + @$internal + @override + $FutureProviderElement $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + final argument = this.argument as (Uri, ResponseErrorHandler?); + return serverStatus(ref, argument.$1, argument.$2); + } + + @override + bool operator ==(Object other) { + return other is ServerStatusProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$serverStatusHash() => r'2d9c5d6f970caec555e5322d43a388ea8572619f'; + +/// fetch status of server + +final class ServerStatusFamily extends $Family + with + $FunctionalFamilyOverride< + FutureOr, + (Uri, ResponseErrorHandler?) + > { + ServerStatusFamily._() + : super( + retry: null, + name: r'serverStatusProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); /// fetch status of server - /// - /// Copied from [serverStatus]. + ServerStatusProvider call( Uri baseUrl, [ - void Function(Response, [Object?])? responseErrorHandler, - ]) { - return ServerStatusProvider( - baseUrl, - responseErrorHandler, - ); - } + ResponseErrorHandler? responseErrorHandler, + ]) => ServerStatusProvider._( + argument: (baseUrl, responseErrorHandler), + from: this, + ); @override - ServerStatusProvider getProviderOverride( - covariant ServerStatusProvider provider, - ) { - return call( - provider.baseUrl, - provider.responseErrorHandler, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'serverStatusProvider'; + String toString() => r'serverStatusProvider'; } -/// fetch status of server -/// -/// Copied from [serverStatus]. -class ServerStatusProvider - extends AutoDisposeFutureProvider { - /// fetch status of server - /// - /// Copied from [serverStatus]. - ServerStatusProvider( - Uri baseUrl, [ - void Function(Response, [Object?])? responseErrorHandler, - ]) : this._internal( - (ref) => serverStatus( - ref as ServerStatusRef, - baseUrl, - responseErrorHandler, - ), - from: serverStatusProvider, - name: r'serverStatusProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$serverStatusHash, - dependencies: ServerStatusFamily._dependencies, - allTransitiveDependencies: - ServerStatusFamily._allTransitiveDependencies, - baseUrl: baseUrl, - responseErrorHandler: responseErrorHandler, - ); +/// fetch the personalized view - ServerStatusProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.baseUrl, - required this.responseErrorHandler, - }) : super.internal(); +@ProviderFor(PersonalizedView) +final personalizedViewProvider = PersonalizedViewProvider._(); - final Uri baseUrl; - final void Function(Response, [Object?])? responseErrorHandler; - - @override - Override overrideWith( - FutureOr Function(ServerStatusRef provider) create, - ) { - return ProviderOverride( - origin: this, - override: ServerStatusProvider._internal( - (ref) => create(ref as ServerStatusRef), - from: from, - name: null, +/// fetch the personalized view +final class PersonalizedViewProvider + extends $StreamNotifierProvider> { + /// fetch the personalized view + PersonalizedViewProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'personalizedViewProvider', + isAutoDispose: true, dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - baseUrl: baseUrl, - responseErrorHandler: responseErrorHandler, - ), - ); - } + $allTransitiveDependencies: null, + ); @override - AutoDisposeFutureProviderElement createElement() { - return _ServerStatusProviderElement(this); - } + String debugGetCreateSourceHash() => _$personalizedViewHash(); + @$internal @override - bool operator ==(Object other) { - return other is ServerStatusProvider && - other.baseUrl == baseUrl && - other.responseErrorHandler == responseErrorHandler; - } + PersonalizedView create() => PersonalizedView(); +} +String _$personalizedViewHash() => r'425e89d99d7e4712b4d6a688f3a12442bd66584f'; + +/// fetch the personalized view + +abstract class _$PersonalizedView extends $StreamNotifier> { + Stream> build(); + @$mustCallSuper @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, baseUrl.hashCode); - hash = _SystemHash.combine(hash, responseErrorHandler.hashCode); - - return _SystemHash.finish(hash); + void runBuild() { + final ref = this.ref as $Ref>, List>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier>, List>, + AsyncValue>, + Object?, + Object? + >; + element.handleCreate(ref, build); } } -mixin ServerStatusRef on AutoDisposeFutureProviderRef { - /// The parameter `baseUrl` of this provider. - Uri get baseUrl; +/// fetch continue listening audiobooks - /// The parameter `responseErrorHandler` of this provider. - void Function(Response, [Object?])? get responseErrorHandler; -} +@ProviderFor(fetchContinueListening) +final fetchContinueListeningProvider = FetchContinueListeningProvider._(); -class _ServerStatusProviderElement - extends AutoDisposeFutureProviderElement - with ServerStatusRef { - _ServerStatusProviderElement(super.provider); +/// fetch continue listening audiobooks + +final class FetchContinueListeningProvider + extends + $FunctionalProvider< + AsyncValue, + GetUserSessionsResponse, + FutureOr + > + with + $FutureModifier, + $FutureProvider { + /// fetch continue listening audiobooks + FetchContinueListeningProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'fetchContinueListeningProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); @override - Uri get baseUrl => (origin as ServerStatusProvider).baseUrl; + String debugGetCreateSourceHash() => _$fetchContinueListeningHash(); + + @$internal @override - void Function(Response, [Object?])? get responseErrorHandler => - (origin as ServerStatusProvider).responseErrorHandler; + $FutureProviderElement $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return fetchContinueListening(ref); + } } String _$fetchContinueListeningHash() => - r'f65fe3ac3a31b8ac074330525c5d2cc4b526802d'; + r'50aeb77369eda38d496b2f56f3df2aea135dab45'; -/// fetch continue listening audiobooks -/// -/// Copied from [fetchContinueListening]. -@ProviderFor(fetchContinueListening) -final fetchContinueListeningProvider = - AutoDisposeFutureProvider.internal( - fetchContinueListening, - name: r'fetchContinueListeningProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$fetchContinueListeningHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef FetchContinueListeningRef - = AutoDisposeFutureProviderRef; -String _$meHash() => r'bdc664c4fd867ad13018fa769ce7a6913248c44f'; - -/// See also [me]. @ProviderFor(me) -final meProvider = AutoDisposeFutureProvider.internal( - me, - name: r'meProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$meHash, - dependencies: null, - allTransitiveDependencies: null, -); +final meProvider = MeProvider._(); -typedef MeRef = AutoDisposeFutureProviderRef; -String _$personalizedViewHash() => r'4c392ece4650bdc36d7195a0ddb8810e8fe4caa9'; +final class MeProvider + extends $FunctionalProvider, User, FutureOr> + with $FutureModifier, $FutureProvider { + MeProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'meProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); -/// fetch the personalized view -/// -/// Copied from [PersonalizedView]. -@ProviderFor(PersonalizedView) -final personalizedViewProvider = - AutoDisposeStreamNotifierProvider>.internal( - PersonalizedView.new, - name: r'personalizedViewProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$personalizedViewHash, - dependencies: null, - allTransitiveDependencies: null, -); + @override + String debugGetCreateSourceHash() => _$meHash(); -typedef _$PersonalizedView = AutoDisposeStreamNotifier>; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return me(ref); + } +} + +String _$meHash() => r'b3b6d6d940b465c60d0c29cd6e81ba2fcccab186'; + +@ProviderFor(login) +final loginProvider = LoginFamily._(); + +final class LoginProvider + extends + $FunctionalProvider< + AsyncValue, + LoginResponse?, + FutureOr + > + with $FutureModifier, $FutureProvider { + LoginProvider._({ + required LoginFamily super.from, + required AuthenticatedUser? super.argument, + }) : super( + retry: null, + name: r'loginProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$loginHash(); + + @override + String toString() { + return r'loginProvider' + '' + '($argument)'; + } + + @$internal + @override + $FutureProviderElement $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + final argument = this.argument as AuthenticatedUser?; + return login(ref, user: argument); + } + + @override + bool operator ==(Object other) { + return other is LoginProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$loginHash() => r'99410c2bed9c8f412c7b47c4e655db64e0054be2'; + +final class LoginFamily extends $Family + with + $FunctionalFamilyOverride< + FutureOr, + AuthenticatedUser? + > { + LoginFamily._() + : super( + retry: null, + name: r'loginProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + LoginProvider call({AuthenticatedUser? user}) => + LoginProvider._(argument: user, from: this); + + @override + String toString() => r'loginProvider'; +} diff --git a/lib/api/authenticated_user_provider.g.dart b/lib/api/authenticated_user_provider.g.dart deleted file mode 100644 index 7fca094..0000000 --- a/lib/api/authenticated_user_provider.g.dart +++ /dev/null @@ -1,28 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'authenticated_user_provider.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$authenticatedUserHash() => r'308f19b33ae04af6340fb83167fa64aa23400a09'; - -/// provides with a set of authenticated users -/// -/// Copied from [AuthenticatedUser]. -@ProviderFor(AuthenticatedUser) -final authenticatedUserProvider = AutoDisposeNotifierProvider>.internal( - AuthenticatedUser.new, - name: r'authenticatedUserProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$authenticatedUserHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$AuthenticatedUser = AutoDisposeNotifier>; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/api/authenticated_user_provider.dart b/lib/api/authenticated_users_provider.dart similarity index 69% rename from lib/api/authenticated_user_provider.dart rename to lib/api/authenticated_users_provider.dart index c10f799..6d437e0 100644 --- a/lib/api/authenticated_user_provider.dart +++ b/lib/api/authenticated_users_provider.dart @@ -5,21 +5,21 @@ import 'package:vaani/api/server_provider.dart' import 'package:vaani/db/storage.dart'; import 'package:vaani/settings/api_settings_provider.dart'; import 'package:vaani/settings/models/audiobookshelf_server.dart'; -import 'package:vaani/settings/models/authenticated_user.dart' - as model; +import 'package:vaani/settings/models/authenticated_user.dart' as model; +import 'package:vaani/shared/extensions/obfuscation.dart'; -part 'authenticated_user_provider.g.dart'; +part 'authenticated_users_provider.g.dart'; final _box = AvailableHiveBoxes.authenticatedUserBox; -final _logger = Logger('authenticated_user_provider'); +final _logger = Logger('authenticated_users_provider'); /// provides with a set of authenticated users @riverpod -class AuthenticatedUser extends _$AuthenticatedUser { +class AuthenticatedUsers extends _$AuthenticatedUsers { @override Set build() { - ref.listenSelf((_, __) { + listenSelf((_, __) { writeStateToBox(); }); // get the app settings @@ -35,7 +35,7 @@ class AuthenticatedUser extends _$AuthenticatedUser { Set readFromBoxOrCreate() { if (_box.isNotEmpty) { final foundData = _box.getRange(0, _box.length); - _logger.fine('found users in box: $foundData'); + _logger.fine('found users in box: ${foundData.obfuscate()}'); return foundData.toSet(); } else { _logger.fine('no settings found in box'); @@ -49,18 +49,17 @@ class AuthenticatedUser extends _$AuthenticatedUser { return; } _box.addAll(state); - _logger.fine('writing state to box: $state'); + _logger.fine('writing state to box: ${state.obfuscate()}'); } void addUser(model.AuthenticatedUser user, {bool setActive = false}) { state = state..add(user); + ref.invalidateSelf(); if (setActive) { final apiSettings = ref.read(apiSettingsProvider); - ref.read(apiSettingsProvider.notifier).updateState( - apiSettings.copyWith( - activeUser: user, - ), - ); + ref + .read(apiSettingsProvider.notifier) + .updateState(apiSettings.copyWith(activeUser: user)); } } @@ -80,11 +79,12 @@ class AuthenticatedUser extends _$AuthenticatedUser { // also remove the user from the active user final apiSettings = ref.read(apiSettingsProvider); if (apiSettings.activeUser == user) { - ref.read(apiSettingsProvider.notifier).updateState( - apiSettings.copyWith( - activeUser: null, - ), - ); + // replace the active user with the first user in the list + // or null if there are no users left + final newActiveUser = state.isNotEmpty ? state.first : null; + ref + .read(apiSettingsProvider.notifier) + .updateState(apiSettings.copyWith(activeUser: newActiveUser)); } } } diff --git a/lib/api/authenticated_users_provider.g.dart b/lib/api/authenticated_users_provider.g.dart new file mode 100644 index 0000000..8e3021f --- /dev/null +++ b/lib/api/authenticated_users_provider.g.dart @@ -0,0 +1,75 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'authenticated_users_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// provides with a set of authenticated users + +@ProviderFor(AuthenticatedUsers) +final authenticatedUsersProvider = AuthenticatedUsersProvider._(); + +/// provides with a set of authenticated users +final class AuthenticatedUsersProvider + extends + $NotifierProvider> { + /// provides with a set of authenticated users + AuthenticatedUsersProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'authenticatedUsersProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$authenticatedUsersHash(); + + @$internal + @override + AuthenticatedUsers create() => AuthenticatedUsers(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Set value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider>(value), + ); + } +} + +String _$authenticatedUsersHash() => + r'c5e82cc70ffc31a0d315e3db9e07a141c583471e'; + +/// provides with a set of authenticated users + +abstract class _$AuthenticatedUsers + extends $Notifier> { + Set build(); + @$mustCallSuper + @override + void runBuild() { + final ref = + this.ref + as $Ref, Set>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier< + Set, + Set + >, + Set, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/api/image_provider.dart b/lib/api/image_provider.dart index 9707542..79fac06 100644 --- a/lib/api/image_provider.dart +++ b/lib/api/image_provider.dart @@ -27,7 +27,8 @@ class CoverImage extends _$CoverImage { // await Future.delayed(const Duration(seconds: 2)); // try to get the image from the cache - final file = await imageCacheManager.getFileFromMemory(itemId) ?? + final file = + await imageCacheManager.getFileFromMemory(itemId) ?? await imageCacheManager.getFileFromCache(itemId); if (file != null) { @@ -44,9 +45,7 @@ class CoverImage extends _$CoverImage { ); return; } else { - _logger.fine( - 'cover image stale for $itemId, fetching from the server', - ); + _logger.fine('cover image stale for $itemId, fetching from the server'); } } else { _logger.fine('cover image not found in cache for $itemId'); diff --git a/lib/api/image_provider.g.dart b/lib/api/image_provider.g.dart index 5fa646e..02ccad8 100644 --- a/lib/api/image_provider.g.dart +++ b/lib/api/image_provider.g.dart @@ -6,167 +6,94 @@ part of 'image_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$coverImageHash() => r'89cc4783cbc76bb41beae34384d92fb277135c75'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -abstract class _$CoverImage extends BuildlessStreamNotifier { - late final String itemId; - - Stream build( - String itemId, - ); -} - -/// See also [CoverImage]. @ProviderFor(CoverImage) -const coverImageProvider = CoverImageFamily(); +final coverImageProvider = CoverImageFamily._(); -/// See also [CoverImage]. -class CoverImageFamily extends Family> { - /// See also [CoverImage]. - const CoverImageFamily(); +final class CoverImageProvider + extends $StreamNotifierProvider { + CoverImageProvider._({ + required CoverImageFamily super.from, + required String super.argument, + }) : super( + retry: null, + name: r'coverImageProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); - /// See also [CoverImage]. - CoverImageProvider call( - String itemId, - ) { - return CoverImageProvider( - itemId, - ); + @override + String debugGetCreateSourceHash() => _$coverImageHash(); + + @override + String toString() { + return r'coverImageProvider' + '' + '($argument)'; } + @$internal @override - CoverImageProvider getProviderOverride( - covariant CoverImageProvider provider, - ) { - return call( - provider.itemId, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'coverImageProvider'; -} - -/// See also [CoverImage]. -class CoverImageProvider - extends StreamNotifierProviderImpl { - /// See also [CoverImage]. - CoverImageProvider( - String itemId, - ) : this._internal( - () => CoverImage()..itemId = itemId, - from: coverImageProvider, - name: r'coverImageProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$coverImageHash, - dependencies: CoverImageFamily._dependencies, - allTransitiveDependencies: - CoverImageFamily._allTransitiveDependencies, - itemId: itemId, - ); - - CoverImageProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.itemId, - }) : super.internal(); - - final String itemId; - - @override - Stream runNotifierBuild( - covariant CoverImage notifier, - ) { - return notifier.build( - itemId, - ); - } - - @override - Override overrideWith(CoverImage Function() create) { - return ProviderOverride( - origin: this, - override: CoverImageProvider._internal( - () => create()..itemId = itemId, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - itemId: itemId, - ), - ); - } - - @override - StreamNotifierProviderElement createElement() { - return _CoverImageProviderElement(this); - } + CoverImage create() => CoverImage(); @override bool operator ==(Object other) { - return other is CoverImageProvider && other.itemId == itemId; + return other is CoverImageProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, itemId.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -mixin CoverImageRef on StreamNotifierProviderRef { - /// The parameter `itemId` of this provider. - String get itemId; -} +String _$coverImageHash() => r'89cc4783cbc76bb41beae34384d92fb277135c75'; -class _CoverImageProviderElement - extends StreamNotifierProviderElement - with CoverImageRef { - _CoverImageProviderElement(super.provider); +final class CoverImageFamily extends $Family + with + $ClassFamilyOverride< + CoverImage, + AsyncValue, + Uint8List, + Stream, + String + > { + CoverImageFamily._() + : super( + retry: null, + name: r'coverImageProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: false, + ); + + CoverImageProvider call(String itemId) => + CoverImageProvider._(argument: itemId, from: this); @override - String get itemId => (origin as CoverImageProvider).itemId; + String toString() => r'coverImageProvider'; +} + +abstract class _$CoverImage extends $StreamNotifier { + late final _$args = ref.$arg as String; + String get itemId => _$args; + + Stream build(String itemId); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref, Uint8List>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, Uint8List>, + AsyncValue, + Object?, + Object? + >; + element.handleCreate(ref, () => build(_$args)); + } } -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/api/library_item_provider.dart b/lib/api/library_item_provider.dart index 013f62e..5a4b1a3 100644 --- a/lib/api/library_item_provider.dart +++ b/lib/api/library_item_provider.dart @@ -26,7 +26,8 @@ class LibraryItem extends _$LibraryItem { // look for the item in the cache final key = CacheKey.libraryItem(id); - final cachedFile = await apiResponseCacheManager.getFileFromMemory(key) ?? + final cachedFile = + await apiResponseCacheManager.getFileFromMemory(key) ?? await apiResponseCacheManager.getFileFromCache(key); if (cachedFile != null) { _logger.fine( diff --git a/lib/api/library_item_provider.g.dart b/lib/api/library_item_provider.g.dart index af297d0..df8fe0e 100644 --- a/lib/api/library_item_provider.g.dart +++ b/lib/api/library_item_provider.g.dart @@ -6,182 +6,112 @@ part of 'library_item_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$libraryItemHash() => r'a3cfa7f912e9498a70b5782899018b6964d6445c'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -abstract class _$LibraryItem - extends BuildlessStreamNotifier { - late final String id; - - Stream build( - String id, - ); -} - +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning /// provides the library item for the given id -/// -/// Copied from [LibraryItem]. + @ProviderFor(LibraryItem) -const libraryItemProvider = LibraryItemFamily(); +final libraryItemProvider = LibraryItemFamily._(); /// provides the library item for the given id -/// -/// Copied from [LibraryItem]. -class LibraryItemFamily - extends Family> { +final class LibraryItemProvider + extends $StreamNotifierProvider { /// provides the library item for the given id - /// - /// Copied from [LibraryItem]. - const LibraryItemFamily(); + LibraryItemProvider._({ + required LibraryItemFamily super.from, + required String super.argument, + }) : super( + retry: null, + name: r'libraryItemProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); - /// provides the library item for the given id - /// - /// Copied from [LibraryItem]. - LibraryItemProvider call( - String id, - ) { - return LibraryItemProvider( - id, - ); + @override + String debugGetCreateSourceHash() => _$libraryItemHash(); + + @override + String toString() { + return r'libraryItemProvider' + '' + '($argument)'; } + @$internal @override - LibraryItemProvider getProviderOverride( - covariant LibraryItemProvider provider, - ) { - return call( - provider.id, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'libraryItemProvider'; -} - -/// provides the library item for the given id -/// -/// Copied from [LibraryItem]. -class LibraryItemProvider extends StreamNotifierProviderImpl { - /// provides the library item for the given id - /// - /// Copied from [LibraryItem]. - LibraryItemProvider( - String id, - ) : this._internal( - () => LibraryItem()..id = id, - from: libraryItemProvider, - name: r'libraryItemProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$libraryItemHash, - dependencies: LibraryItemFamily._dependencies, - allTransitiveDependencies: - LibraryItemFamily._allTransitiveDependencies, - id: id, - ); - - LibraryItemProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); - - final String id; - - @override - Stream runNotifierBuild( - covariant LibraryItem notifier, - ) { - return notifier.build( - id, - ); - } - - @override - Override overrideWith(LibraryItem Function() create) { - return ProviderOverride( - origin: this, - override: LibraryItemProvider._internal( - () => create()..id = id, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), - ); - } - - @override - StreamNotifierProviderElement - createElement() { - return _LibraryItemProviderElement(this); - } + LibraryItem create() => LibraryItem(); @override bool operator ==(Object other) { - return other is LibraryItemProvider && other.id == id; + return other is LibraryItemProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -mixin LibraryItemRef - on StreamNotifierProviderRef { - /// The parameter `id` of this provider. - String get id; -} +String _$libraryItemHash() => r'a3cfa7f912e9498a70b5782899018b6964d6445c'; -class _LibraryItemProviderElement extends StreamNotifierProviderElement< - LibraryItem, shelfsdk.LibraryItemExpanded> with LibraryItemRef { - _LibraryItemProviderElement(super.provider); +/// provides the library item for the given id + +final class LibraryItemFamily extends $Family + with + $ClassFamilyOverride< + LibraryItem, + AsyncValue, + shelfsdk.LibraryItemExpanded, + Stream, + String + > { + LibraryItemFamily._() + : super( + retry: null, + name: r'libraryItemProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: false, + ); + + /// provides the library item for the given id + + LibraryItemProvider call(String id) => + LibraryItemProvider._(argument: id, from: this); @override - String get id => (origin as LibraryItemProvider).id; + String toString() => r'libraryItemProvider'; +} + +/// provides the library item for the given id + +abstract class _$LibraryItem + extends $StreamNotifier { + late final _$args = ref.$arg as String; + String get id => _$args; + + Stream build(String id); + @$mustCallSuper + @override + void runBuild() { + final ref = + this.ref + as $Ref< + AsyncValue, + shelfsdk.LibraryItemExpanded + >; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier< + AsyncValue, + shelfsdk.LibraryItemExpanded + >, + AsyncValue, + Object?, + Object? + >; + element.handleCreate(ref, () => build(_$args)); + } } -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/api/library_provider.dart b/lib/api/library_provider.dart new file mode 100644 index 0000000..3ba5399 --- /dev/null +++ b/lib/api/library_provider.dart @@ -0,0 +1,60 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart' + show Ref, ProviderListenableSelect; +import 'package:logging/logging.dart' show Logger; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import 'package:shelfsdk/audiobookshelf_api.dart' show Library; +import 'package:vaani/api/api_provider.dart' show authenticatedApiProvider; +import 'package:vaani/settings/api_settings_provider.dart' + show apiSettingsProvider; +part 'library_provider.g.dart'; + +final _logger = Logger('LibraryProvider'); + +@riverpod +Future library(Ref ref, String id) async { + final api = ref.watch(authenticatedApiProvider); + final library = await api.libraries.get(libraryId: id); + if (library == null) { + _logger.warning('No library found through id: $id'); + // try to get the library from the list of libraries + final libraries = await ref.watch(librariesProvider.future); + for (final lib in libraries) { + if (lib.id == id) { + return lib; + } + } + _logger.warning('No library found in the list of libraries'); + return null; + } + _logger.fine('Fetched library: $library'); + return library.library; +} + +@riverpod +Future currentLibrary(Ref ref) async { + final libraryId = ref.watch( + apiSettingsProvider.select((s) => s.activeLibraryId), + ); + if (libraryId == null) { + _logger.warning('No active library id found'); + return null; + } + return await ref.watch(libraryProvider(libraryId).future); +} + +@riverpod +class Libraries extends _$Libraries { + @override + FutureOr> build() async { + final api = ref.watch(authenticatedApiProvider); + final libraries = await api.libraries.getAll(); + if (libraries == null) { + _logger.warning('Failed to fetch libraries'); + return []; + } + _logger.fine('Fetched ${libraries.length} libraries'); + ref.keepAlive(); + return libraries; + } +} diff --git a/lib/api/library_provider.g.dart b/lib/api/library_provider.g.dart new file mode 100644 index 0000000..df8fbfd --- /dev/null +++ b/lib/api/library_provider.g.dart @@ -0,0 +1,158 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'library_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(library) +final libraryProvider = LibraryFamily._(); + +final class LibraryProvider + extends + $FunctionalProvider, Library?, FutureOr> + with $FutureModifier, $FutureProvider { + LibraryProvider._({ + required LibraryFamily super.from, + required String super.argument, + }) : super( + retry: null, + name: r'libraryProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$libraryHash(); + + @override + String toString() { + return r'libraryProvider' + '' + '($argument)'; + } + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + final argument = this.argument as String; + return library(ref, argument); + } + + @override + bool operator ==(Object other) { + return other is LibraryProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$libraryHash() => r'f8a34100acb58f02fa958c71a629577bf815710e'; + +final class LibraryFamily extends $Family + with $FunctionalFamilyOverride, String> { + LibraryFamily._() + : super( + retry: null, + name: r'libraryProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + LibraryProvider call(String id) => + LibraryProvider._(argument: id, from: this); + + @override + String toString() => r'libraryProvider'; +} + +@ProviderFor(currentLibrary) +final currentLibraryProvider = CurrentLibraryProvider._(); + +final class CurrentLibraryProvider + extends + $FunctionalProvider, Library?, FutureOr> + with $FutureModifier, $FutureProvider { + CurrentLibraryProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'currentLibraryProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$currentLibraryHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return currentLibrary(ref); + } +} + +String _$currentLibraryHash() => r'658498a531e04a01e2b3915a3319101285601118'; + +@ProviderFor(Libraries) +final librariesProvider = LibrariesProvider._(); + +final class LibrariesProvider + extends $AsyncNotifierProvider> { + LibrariesProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'librariesProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$librariesHash(); + + @$internal + @override + Libraries create() => Libraries(); +} + +String _$librariesHash() => r'95ebd4d1ac0cc2acf7617dc22895eff0ca30600f'; + +abstract class _$Libraries extends $AsyncNotifier> { + FutureOr> build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref>, List>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier>, List>, + AsyncValue>, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/api/server_provider.dart b/lib/api/server_provider.dart index 4ad4b1e..41c82f1 100644 --- a/lib/api/server_provider.dart +++ b/lib/api/server_provider.dart @@ -1,16 +1,17 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; -import 'package:vaani/api/authenticated_user_provider.dart'; +import 'package:vaani/api/authenticated_users_provider.dart'; import 'package:vaani/db/storage.dart'; import 'package:vaani/settings/api_settings_provider.dart'; -import 'package:vaani/settings/models/audiobookshelf_server.dart' - as model; +import 'package:vaani/settings/models/audiobookshelf_server.dart' as model; +import 'package:vaani/shared/extensions/obfuscation.dart'; part 'server_provider.g.dart'; final _box = AvailableHiveBoxes.serverBox; +final _logger = Logger('AudiobookShelfServerProvider'); + class ServerAlreadyExistsException implements Exception { final model.AudiobookShelfServer server; @@ -27,7 +28,7 @@ class ServerAlreadyExistsException implements Exception { class AudiobookShelfServer extends _$AudiobookShelfServer { @override Set build() { - ref.listenSelf((_, __) { + listenSelf((_, __) { writeStateToBox(); }); // get the app settings @@ -47,10 +48,10 @@ class AudiobookShelfServer extends _$AudiobookShelfServer { Set readFromBoxOrCreate() { if (_box.isNotEmpty) { final foundServers = _box.getRange(0, _box.length); - debugPrint('found servers in box: $foundServers'); - return foundServers.whereNotNull().toSet(); + _logger.info('found servers in box: ${foundServers.obfuscate()}'); + return foundServers.nonNulls.toSet(); } else { - debugPrint('no settings found in box'); + _logger.info('no settings found in box'); return {}; } } @@ -61,7 +62,7 @@ class AudiobookShelfServer extends _$AudiobookShelfServer { return; } _box.addAll(state); - debugPrint('writing state to box: $state'); + _logger.info('writing state to box: ${state.obfuscate()}'); } void addServer(model.AudiobookShelfServer server) { @@ -71,23 +72,21 @@ class AudiobookShelfServer extends _$AudiobookShelfServer { state = {...state, server}; } - void removeServer(model.AudiobookShelfServer server, - { + void removeServer( + model.AudiobookShelfServer server, { bool removeUsers = false, }) { state = state.where((s) => s != server).toSet(); // remove the server from the active server final apiSettings = ref.read(apiSettingsProvider); if (apiSettings.activeServer == server) { - ref.read(apiSettingsProvider.notifier).updateState( - apiSettings.copyWith( - activeServer: null, - ), - ); + ref + .read(apiSettingsProvider.notifier) + .updateState(apiSettings.copyWith(activeServer: null)); } // remove the users of this server if (removeUsers) { - ref.read(authenticatedUserProvider.notifier).removeUsersOfServer(server); + ref.read(authenticatedUsersProvider.notifier).removeUsersOfServer(server); } } diff --git a/lib/api/server_provider.g.dart b/lib/api/server_provider.g.dart index 69478cc..16eddd0 100644 --- a/lib/api/server_provider.g.dart +++ b/lib/api/server_provider.g.dart @@ -6,25 +6,78 @@ part of 'server_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$audiobookShelfServerHash() => - r'f0d645bb42233c59886bc43fdc473897484ceca1'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// provides with a set of servers added by the user + +@ProviderFor(AudiobookShelfServer) +final audiobookShelfServerProvider = AudiobookShelfServerProvider._(); /// provides with a set of servers added by the user -/// -/// Copied from [AudiobookShelfServer]. -@ProviderFor(AudiobookShelfServer) -final audiobookShelfServerProvider = AutoDisposeNotifierProvider< - AudiobookShelfServer, Set>.internal( - AudiobookShelfServer.new, - name: r'audiobookShelfServerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$audiobookShelfServerHash, - dependencies: null, - allTransitiveDependencies: null, -); +final class AudiobookShelfServerProvider + extends + $NotifierProvider< + AudiobookShelfServer, + Set + > { + /// provides with a set of servers added by the user + AudiobookShelfServerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'audiobookShelfServerProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); -typedef _$AudiobookShelfServer - = AutoDisposeNotifier>; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member + @override + String debugGetCreateSourceHash() => _$audiobookShelfServerHash(); + + @$internal + @override + AudiobookShelfServer create() => AudiobookShelfServer(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Set value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider>( + value, + ), + ); + } +} + +String _$audiobookShelfServerHash() => + r'144817dcb3704b80c5b60763167fcf932f00c29c'; + +/// provides with a set of servers added by the user + +abstract class _$AudiobookShelfServer + extends $Notifier> { + Set build(); + @$mustCallSuper + @override + void runBuild() { + final ref = + this.ref + as $Ref< + Set, + Set + >; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier< + Set, + Set + >, + Set, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/constants/hero_tag_conventions.dart b/lib/constants/hero_tag_conventions.dart index 6934c00..4ec40a9 100644 --- a/lib/constants/hero_tag_conventions.dart +++ b/lib/constants/hero_tag_conventions.dart @@ -10,5 +10,4 @@ class HeroTagPrefixes { static const String bookTitle = 'book_title_'; static const String narratorName = 'narrator_name_'; static const String libraryItemPlayButton = 'library_item_play_button_'; - } diff --git a/lib/db/available_boxes.dart b/lib/db/available_boxes.dart index ff01acc..77b0c3f 100644 --- a/lib/db/available_boxes.dart +++ b/lib/db/available_boxes.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart' show immutable; -import 'package:hive/hive.dart'; +import 'package:hive_plus_secure/hive_plus_secure.dart'; import 'package:vaani/features/per_book_settings/models/book_settings.dart'; import 'package:vaani/settings/models/models.dart'; @@ -14,14 +14,17 @@ class AvailableHiveBoxes { static final apiSettingsBox = Hive.box(name: 'apiSettings'); /// stores the a list of [AudiobookShelfServer] - static final serverBox = - Hive.box(name: 'audiobookShelfServer'); + static final serverBox = Hive.box( + name: 'audiobookShelfServer', + ); /// stores the a list of [AuthenticatedUser] - static final authenticatedUserBox = - Hive.box(name: 'authenticatedUser'); + static final authenticatedUserBox = Hive.box( + name: 'authenticatedUser', + ); /// stores the a list of [BookSettings] - static final individualBookSettingsBox = - Hive.box(name: 'bookSettings'); + static final individualBookSettingsBox = Hive.box( + name: 'bookSettings', + ); } diff --git a/lib/db/cache/schemas/image.dart b/lib/db/cache/schemas/image.dart index 9e93073..fef8025 100644 --- a/lib/db/cache/schemas/image.dart +++ b/lib/db/cache/schemas/image.dart @@ -1,39 +1,39 @@ -import 'package:isar/isar.dart'; +// import 'package:isar/isar.dart'; -part 'image.g.dart'; +// part 'image.g.dart'; -/// Represents a cover image for a library item -/// -/// stores 2 paths, one is thumbnail and the other is the full size image -/// both are optional -/// also stores last fetched date for the image -/// Id is passed as a parameter to the collection annotation (the lib_item_id) -/// also index the id -/// This is because the image is a part of the library item and the library item -/// is the parent of the image -@Collection(ignore: {'path'}) -@Name('CacheImage') -class Image { - @Id() - int id; +// /// Represents a cover image for a library item +// /// +// /// stores 2 paths, one is thumbnail and the other is the full size image +// /// both are optional +// /// also stores last fetched date for the image +// /// Id is passed as a parameter to the collection annotation (the lib_item_id) +// /// also index the id +// /// This is because the image is a part of the library item and the library item +// /// is the parent of the image +// @Collection(ignore: {'path'}) +// @Name('CacheImage') +// class Image { +// @Id() +// int id; - String? thumbnailPath; - String? imagePath; - DateTime lastSaved; +// String? thumbnailPath; +// String? imagePath; +// DateTime lastSaved; - Image({ - required this.id, - this.thumbnailPath, - this.imagePath, - }) : lastSaved = DateTime.now(); +// Image({ +// required this.id, +// this.thumbnailPath, +// this.imagePath, +// }) : lastSaved = DateTime.now(); - /// returns the path to the image - String? get path => thumbnailPath ?? imagePath; +// /// returns the path to the image +// String? get path => thumbnailPath ?? imagePath; - /// automatically updates the last fetched date when saving a new path - void updatePath(String? thumbnailPath, String? imagePath) async { - this.thumbnailPath = thumbnailPath; - this.imagePath = imagePath; - lastSaved = DateTime.now(); - } -} +// /// automatically updates the last fetched date when saving a new path +// void updatePath(String? thumbnailPath, String? imagePath) async { +// this.thumbnailPath = thumbnailPath; +// this.imagePath = imagePath; +// lastSaved = DateTime.now(); +// } +// } diff --git a/lib/db/cache/schemas/image.g.dart b/lib/db/cache/schemas/image.g.dart deleted file mode 100644 index 555ce5e..0000000 --- a/lib/db/cache/schemas/image.g.dart +++ /dev/null @@ -1,1009 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'image.dart'; - -// ************************************************************************** -// _IsarCollectionGenerator -// ************************************************************************** - -// coverage:ignore-file -// ignore_for_file: duplicate_ignore, invalid_use_of_protected_member, lines_longer_than_80_chars, constant_identifier_names, avoid_js_rounded_ints, no_leading_underscores_for_local_identifiers, require_trailing_commas, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_in_if_null_operators, library_private_types_in_public_api, prefer_const_constructors -// ignore_for_file: type=lint - -extension GetImageCollection on Isar { - IsarCollection get images => this.collection(); -} - -const ImageSchema = IsarGeneratedSchema( - schema: IsarSchema( - name: 'CacheImage', - idName: 'id', - embedded: false, - properties: [ - IsarPropertySchema( - name: 'thumbnailPath', - type: IsarType.string, - ), - IsarPropertySchema( - name: 'imagePath', - type: IsarType.string, - ), - IsarPropertySchema( - name: 'lastSaved', - type: IsarType.dateTime, - ), - ], - indexes: [], - ), - converter: IsarObjectConverter( - serialize: serializeImage, - deserialize: deserializeImage, - deserializeProperty: deserializeImageProp, - ), - embeddedSchemas: [], -); - -@isarProtected -int serializeImage(IsarWriter writer, Image object) { - { - final value = object.thumbnailPath; - if (value == null) { - IsarCore.writeNull(writer, 1); - } else { - IsarCore.writeString(writer, 1, value); - } - } - { - final value = object.imagePath; - if (value == null) { - IsarCore.writeNull(writer, 2); - } else { - IsarCore.writeString(writer, 2, value); - } - } - IsarCore.writeLong( - writer, 3, object.lastSaved.toUtc().microsecondsSinceEpoch); - return object.id; -} - -@isarProtected -Image deserializeImage(IsarReader reader) { - final int _id; - _id = IsarCore.readId(reader); - final String? _thumbnailPath; - _thumbnailPath = IsarCore.readString(reader, 1); - final String? _imagePath; - _imagePath = IsarCore.readString(reader, 2); - final object = Image( - id: _id, - thumbnailPath: _thumbnailPath, - imagePath: _imagePath, - ); - { - final value = IsarCore.readLong(reader, 3); - if (value == -9223372036854775808) { - object.lastSaved = - DateTime.fromMillisecondsSinceEpoch(0, isUtc: true).toLocal(); - } else { - object.lastSaved = - DateTime.fromMicrosecondsSinceEpoch(value, isUtc: true).toLocal(); - } - } - return object; -} - -@isarProtected -dynamic deserializeImageProp(IsarReader reader, int property) { - switch (property) { - case 0: - return IsarCore.readId(reader); - case 1: - return IsarCore.readString(reader, 1); - case 2: - return IsarCore.readString(reader, 2); - case 3: - { - final value = IsarCore.readLong(reader, 3); - if (value == -9223372036854775808) { - return DateTime.fromMillisecondsSinceEpoch(0, isUtc: true).toLocal(); - } else { - return DateTime.fromMicrosecondsSinceEpoch(value, isUtc: true) - .toLocal(); - } - } - default: - throw ArgumentError('Unknown property: $property'); - } -} - -sealed class _ImageUpdate { - bool call({ - required int id, - String? thumbnailPath, - String? imagePath, - DateTime? lastSaved, - }); -} - -class _ImageUpdateImpl implements _ImageUpdate { - const _ImageUpdateImpl(this.collection); - - final IsarCollection collection; - - @override - bool call({ - required int id, - Object? thumbnailPath = ignore, - Object? imagePath = ignore, - Object? lastSaved = ignore, - }) { - return collection.updateProperties([ - id - ], { - if (thumbnailPath != ignore) 1: thumbnailPath as String?, - if (imagePath != ignore) 2: imagePath as String?, - if (lastSaved != ignore) 3: lastSaved as DateTime?, - }) > - 0; - } -} - -sealed class _ImageUpdateAll { - int call({ - required List id, - String? thumbnailPath, - String? imagePath, - DateTime? lastSaved, - }); -} - -class _ImageUpdateAllImpl implements _ImageUpdateAll { - const _ImageUpdateAllImpl(this.collection); - - final IsarCollection collection; - - @override - int call({ - required List id, - Object? thumbnailPath = ignore, - Object? imagePath = ignore, - Object? lastSaved = ignore, - }) { - return collection.updateProperties(id, { - if (thumbnailPath != ignore) 1: thumbnailPath as String?, - if (imagePath != ignore) 2: imagePath as String?, - if (lastSaved != ignore) 3: lastSaved as DateTime?, - }); - } -} - -extension ImageUpdate on IsarCollection { - _ImageUpdate get update => _ImageUpdateImpl(this); - - _ImageUpdateAll get updateAll => _ImageUpdateAllImpl(this); -} - -sealed class _ImageQueryUpdate { - int call({ - String? thumbnailPath, - String? imagePath, - DateTime? lastSaved, - }); -} - -class _ImageQueryUpdateImpl implements _ImageQueryUpdate { - const _ImageQueryUpdateImpl(this.query, {this.limit}); - - final IsarQuery query; - final int? limit; - - @override - int call({ - Object? thumbnailPath = ignore, - Object? imagePath = ignore, - Object? lastSaved = ignore, - }) { - return query.updateProperties(limit: limit, { - if (thumbnailPath != ignore) 1: thumbnailPath as String?, - if (imagePath != ignore) 2: imagePath as String?, - if (lastSaved != ignore) 3: lastSaved as DateTime?, - }); - } -} - -extension ImageQueryUpdate on IsarQuery { - _ImageQueryUpdate get updateFirst => _ImageQueryUpdateImpl(this, limit: 1); - - _ImageQueryUpdate get updateAll => _ImageQueryUpdateImpl(this); -} - -class _ImageQueryBuilderUpdateImpl implements _ImageQueryUpdate { - const _ImageQueryBuilderUpdateImpl(this.query, {this.limit}); - - final QueryBuilder query; - final int? limit; - - @override - int call({ - Object? thumbnailPath = ignore, - Object? imagePath = ignore, - Object? lastSaved = ignore, - }) { - final q = query.build(); - try { - return q.updateProperties(limit: limit, { - if (thumbnailPath != ignore) 1: thumbnailPath as String?, - if (imagePath != ignore) 2: imagePath as String?, - if (lastSaved != ignore) 3: lastSaved as DateTime?, - }); - } finally { - q.close(); - } - } -} - -extension ImageQueryBuilderUpdate on QueryBuilder { - _ImageQueryUpdate get updateFirst => - _ImageQueryBuilderUpdateImpl(this, limit: 1); - - _ImageQueryUpdate get updateAll => _ImageQueryBuilderUpdateImpl(this); -} - -extension ImageQueryFilter on QueryBuilder { - QueryBuilder idEqualTo( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - EqualCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder idGreaterThan( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder idGreaterThanOrEqualTo( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterOrEqualCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder idLessThan( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder idLessThanOrEqualTo( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessOrEqualCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder idBetween( - int lower, - int upper, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - BetweenCondition( - property: 0, - lower: lower, - upper: upper, - ), - ); - }); - } - - QueryBuilder thumbnailPathIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const IsNullCondition(property: 1)); - }); - } - - QueryBuilder thumbnailPathIsNotNull() { - return QueryBuilder.apply(not(), (query) { - return query.addFilterCondition(const IsNullCondition(property: 1)); - }); - } - - QueryBuilder thumbnailPathEqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - EqualCondition( - property: 1, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder thumbnailPathGreaterThan( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterCondition( - property: 1, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - thumbnailPathGreaterThanOrEqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterOrEqualCondition( - property: 1, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder thumbnailPathLessThan( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessCondition( - property: 1, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - thumbnailPathLessThanOrEqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessOrEqualCondition( - property: 1, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder thumbnailPathBetween( - String? lower, - String? upper, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - BetweenCondition( - property: 1, - lower: lower, - upper: upper, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder thumbnailPathStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - StartsWithCondition( - property: 1, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder thumbnailPathEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - EndsWithCondition( - property: 1, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder thumbnailPathContains( - String value, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - ContainsCondition( - property: 1, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder thumbnailPathMatches( - String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - MatchesCondition( - property: 1, - wildcard: pattern, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder thumbnailPathIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const EqualCondition( - property: 1, - value: '', - ), - ); - }); - } - - QueryBuilder thumbnailPathIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const GreaterCondition( - property: 1, - value: '', - ), - ); - }); - } - - QueryBuilder imagePathIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const IsNullCondition(property: 2)); - }); - } - - QueryBuilder imagePathIsNotNull() { - return QueryBuilder.apply(not(), (query) { - return query.addFilterCondition(const IsNullCondition(property: 2)); - }); - } - - QueryBuilder imagePathEqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - EqualCondition( - property: 2, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder imagePathGreaterThan( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterCondition( - property: 2, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - imagePathGreaterThanOrEqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterOrEqualCondition( - property: 2, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder imagePathLessThan( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessCondition( - property: 2, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder imagePathLessThanOrEqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessOrEqualCondition( - property: 2, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder imagePathBetween( - String? lower, - String? upper, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - BetweenCondition( - property: 2, - lower: lower, - upper: upper, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder imagePathStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - StartsWithCondition( - property: 2, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder imagePathEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - EndsWithCondition( - property: 2, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder imagePathContains( - String value, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - ContainsCondition( - property: 2, - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder imagePathMatches( - String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - MatchesCondition( - property: 2, - wildcard: pattern, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder imagePathIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const EqualCondition( - property: 2, - value: '', - ), - ); - }); - } - - QueryBuilder imagePathIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const GreaterCondition( - property: 2, - value: '', - ), - ); - }); - } - - QueryBuilder lastSavedEqualTo( - DateTime value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - EqualCondition( - property: 3, - value: value, - ), - ); - }); - } - - QueryBuilder lastSavedGreaterThan( - DateTime value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterCondition( - property: 3, - value: value, - ), - ); - }); - } - - QueryBuilder - lastSavedGreaterThanOrEqualTo( - DateTime value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterOrEqualCondition( - property: 3, - value: value, - ), - ); - }); - } - - QueryBuilder lastSavedLessThan( - DateTime value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessCondition( - property: 3, - value: value, - ), - ); - }); - } - - QueryBuilder lastSavedLessThanOrEqualTo( - DateTime value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessOrEqualCondition( - property: 3, - value: value, - ), - ); - }); - } - - QueryBuilder lastSavedBetween( - DateTime lower, - DateTime upper, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - BetweenCondition( - property: 3, - lower: lower, - upper: upper, - ), - ); - }); - } -} - -extension ImageQueryObject on QueryBuilder {} - -extension ImageQuerySortBy on QueryBuilder { - QueryBuilder sortById() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(0); - }); - } - - QueryBuilder sortByIdDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(0, sort: Sort.desc); - }); - } - - QueryBuilder sortByThumbnailPath( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addSortBy( - 1, - caseSensitive: caseSensitive, - ); - }); - } - - QueryBuilder sortByThumbnailPathDesc( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addSortBy( - 1, - sort: Sort.desc, - caseSensitive: caseSensitive, - ); - }); - } - - QueryBuilder sortByImagePath( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addSortBy( - 2, - caseSensitive: caseSensitive, - ); - }); - } - - QueryBuilder sortByImagePathDesc( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addSortBy( - 2, - sort: Sort.desc, - caseSensitive: caseSensitive, - ); - }); - } - - QueryBuilder sortByLastSaved() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(3); - }); - } - - QueryBuilder sortByLastSavedDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(3, sort: Sort.desc); - }); - } -} - -extension ImageQuerySortThenBy on QueryBuilder { - QueryBuilder thenById() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(0); - }); - } - - QueryBuilder thenByIdDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(0, sort: Sort.desc); - }); - } - - QueryBuilder thenByThumbnailPath( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(1, caseSensitive: caseSensitive); - }); - } - - QueryBuilder thenByThumbnailPathDesc( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(1, sort: Sort.desc, caseSensitive: caseSensitive); - }); - } - - QueryBuilder thenByImagePath( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(2, caseSensitive: caseSensitive); - }); - } - - QueryBuilder thenByImagePathDesc( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(2, sort: Sort.desc, caseSensitive: caseSensitive); - }); - } - - QueryBuilder thenByLastSaved() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(3); - }); - } - - QueryBuilder thenByLastSavedDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(3, sort: Sort.desc); - }); - } -} - -extension ImageQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByThumbnailPath( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(1, caseSensitive: caseSensitive); - }); - } - - QueryBuilder distinctByImagePath( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(2, caseSensitive: caseSensitive); - }); - } - - QueryBuilder distinctByLastSaved() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(3); - }); - } -} - -extension ImageQueryProperty1 on QueryBuilder { - QueryBuilder idProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(0); - }); - } - - QueryBuilder thumbnailPathProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(1); - }); - } - - QueryBuilder imagePathProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(2); - }); - } - - QueryBuilder lastSavedProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(3); - }); - } -} - -extension ImageQueryProperty2 on QueryBuilder { - QueryBuilder idProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(0); - }); - } - - QueryBuilder thumbnailPathProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(1); - }); - } - - QueryBuilder imagePathProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(2); - }); - } - - QueryBuilder lastSavedProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(3); - }); - } -} - -extension ImageQueryProperty3 - on QueryBuilder { - QueryBuilder idProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(0); - }); - } - - QueryBuilder thumbnailPathProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(1); - }); - } - - QueryBuilder imagePathProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(2); - }); - } - - QueryBuilder lastSavedProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(3); - }); - } -} diff --git a/lib/db/init.dart b/lib/db/init.dart index ad875db..280144e 100644 --- a/lib/db/init.dart +++ b/lib/db/init.dart @@ -1,28 +1,23 @@ -// does the initial setup of the storage - import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; +import 'package:hive_plus_secure/hive_plus_secure.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; +import 'package:vaani/main.dart'; import 'package:vaani/settings/constants.dart'; import 'register_models.dart'; +// does the initial setup of the storage Future initStorage() async { final dir = await getApplicationDocumentsDirectory(); // use vaani as the directory for hive - final storageDir = Directory(p.join( - dir.path, - AppMetadata.appNameLowerCase, - ), - ); + final storageDir = Directory(p.join(dir.path, AppMetadata.appNameLowerCase)); await storageDir.create(recursive: true); Hive.defaultDirectory = storageDir.path; - debugPrint('Hive storage directory init: ${Hive.defaultDirectory}'); + appLogger.config('Hive storage directory init: ${Hive.defaultDirectory}'); await registerModels(); } diff --git a/lib/db/player_prefs/book_prefs.dart b/lib/db/player_prefs/book_prefs.dart index b4e3807..ca610c8 100644 --- a/lib/db/player_prefs/book_prefs.dart +++ b/lib/db/player_prefs/book_prefs.dart @@ -1,29 +1,29 @@ -// a table to track preferences of player for each book -import 'package:isar/isar.dart'; +// // a table to track preferences of player for each book +// import 'package:isar/isar.dart'; -part 'book_prefs.g.dart'; +// part 'book_prefs.g.dart'; -/// stores the preferences of the player for a book -@Collection() -@Name('BookPrefs') -class BookPrefs { - @Id() - int libItemId; +// /// stores the preferences of the player for a book +// @Collection() +// @Name('BookPrefs') +// class BookPrefs { +// @Id() +// int libItemId; - double? speed; - // double? volume; - // Duration? sleepTimer; - // bool? showTotalProgress; - // bool? showChapterProgress; - // bool? useChapterInfo; +// double? speed; +// // double? volume; +// // Duration? sleepTimer; +// // bool? showTotalProgress; +// // bool? showChapterProgress; +// // bool? useChapterInfo; - BookPrefs({ - required this.libItemId, - this.speed, - // this.volume, - // this.sleepTimer, - // this.showTotalProgress, - // this.showChapterProgress, - // this.useChapterInfo, - }); -} +// BookPrefs({ +// required this.libItemId, +// this.speed, +// // this.volume, +// // this.sleepTimer, +// // this.showTotalProgress, +// // this.showChapterProgress, +// // this.useChapterInfo, +// }); +// } diff --git a/lib/db/player_prefs/book_prefs.g.dart b/lib/db/player_prefs/book_prefs.g.dart deleted file mode 100644 index ee012a5..0000000 --- a/lib/db/player_prefs/book_prefs.g.dart +++ /dev/null @@ -1,496 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'book_prefs.dart'; - -// ************************************************************************** -// _IsarCollectionGenerator -// ************************************************************************** - -// coverage:ignore-file -// ignore_for_file: duplicate_ignore, invalid_use_of_protected_member, lines_longer_than_80_chars, constant_identifier_names, avoid_js_rounded_ints, no_leading_underscores_for_local_identifiers, require_trailing_commas, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_in_if_null_operators, library_private_types_in_public_api, prefer_const_constructors -// ignore_for_file: type=lint - -extension GetBookPrefsCollection on Isar { - IsarCollection get bookPrefs => this.collection(); -} - -const BookPrefsSchema = IsarGeneratedSchema( - schema: IsarSchema( - name: 'BookPrefs', - idName: 'libItemId', - embedded: false, - properties: [ - IsarPropertySchema( - name: 'speed', - type: IsarType.double, - ), - ], - indexes: [], - ), - converter: IsarObjectConverter( - serialize: serializeBookPrefs, - deserialize: deserializeBookPrefs, - deserializeProperty: deserializeBookPrefsProp, - ), - embeddedSchemas: [], -); - -@isarProtected -int serializeBookPrefs(IsarWriter writer, BookPrefs object) { - IsarCore.writeDouble(writer, 1, object.speed ?? double.nan); - return object.libItemId; -} - -@isarProtected -BookPrefs deserializeBookPrefs(IsarReader reader) { - final int _libItemId; - _libItemId = IsarCore.readId(reader); - final double? _speed; - { - final value = IsarCore.readDouble(reader, 1); - if (value.isNaN) { - _speed = null; - } else { - _speed = value; - } - } - final object = BookPrefs( - libItemId: _libItemId, - speed: _speed, - ); - return object; -} - -@isarProtected -dynamic deserializeBookPrefsProp(IsarReader reader, int property) { - switch (property) { - case 0: - return IsarCore.readId(reader); - case 1: - { - final value = IsarCore.readDouble(reader, 1); - if (value.isNaN) { - return null; - } else { - return value; - } - } - default: - throw ArgumentError('Unknown property: $property'); - } -} - -sealed class _BookPrefsUpdate { - bool call({ - required int libItemId, - double? speed, - }); -} - -class _BookPrefsUpdateImpl implements _BookPrefsUpdate { - const _BookPrefsUpdateImpl(this.collection); - - final IsarCollection collection; - - @override - bool call({ - required int libItemId, - Object? speed = ignore, - }) { - return collection.updateProperties([ - libItemId - ], { - if (speed != ignore) 1: speed as double?, - }) > - 0; - } -} - -sealed class _BookPrefsUpdateAll { - int call({ - required List libItemId, - double? speed, - }); -} - -class _BookPrefsUpdateAllImpl implements _BookPrefsUpdateAll { - const _BookPrefsUpdateAllImpl(this.collection); - - final IsarCollection collection; - - @override - int call({ - required List libItemId, - Object? speed = ignore, - }) { - return collection.updateProperties(libItemId, { - if (speed != ignore) 1: speed as double?, - }); - } -} - -extension BookPrefsUpdate on IsarCollection { - _BookPrefsUpdate get update => _BookPrefsUpdateImpl(this); - - _BookPrefsUpdateAll get updateAll => _BookPrefsUpdateAllImpl(this); -} - -sealed class _BookPrefsQueryUpdate { - int call({ - double? speed, - }); -} - -class _BookPrefsQueryUpdateImpl implements _BookPrefsQueryUpdate { - const _BookPrefsQueryUpdateImpl(this.query, {this.limit}); - - final IsarQuery query; - final int? limit; - - @override - int call({ - Object? speed = ignore, - }) { - return query.updateProperties(limit: limit, { - if (speed != ignore) 1: speed as double?, - }); - } -} - -extension BookPrefsQueryUpdate on IsarQuery { - _BookPrefsQueryUpdate get updateFirst => - _BookPrefsQueryUpdateImpl(this, limit: 1); - - _BookPrefsQueryUpdate get updateAll => _BookPrefsQueryUpdateImpl(this); -} - -class _BookPrefsQueryBuilderUpdateImpl implements _BookPrefsQueryUpdate { - const _BookPrefsQueryBuilderUpdateImpl(this.query, {this.limit}); - - final QueryBuilder query; - final int? limit; - - @override - int call({ - Object? speed = ignore, - }) { - final q = query.build(); - try { - return q.updateProperties(limit: limit, { - if (speed != ignore) 1: speed as double?, - }); - } finally { - q.close(); - } - } -} - -extension BookPrefsQueryBuilderUpdate - on QueryBuilder { - _BookPrefsQueryUpdate get updateFirst => - _BookPrefsQueryBuilderUpdateImpl(this, limit: 1); - - _BookPrefsQueryUpdate get updateAll => _BookPrefsQueryBuilderUpdateImpl(this); -} - -extension BookPrefsQueryFilter - on QueryBuilder { - QueryBuilder libItemIdEqualTo( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - EqualCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder - libItemIdGreaterThan( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder - libItemIdGreaterThanOrEqualTo( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterOrEqualCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder libItemIdLessThan( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder - libItemIdLessThanOrEqualTo( - int value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessOrEqualCondition( - property: 0, - value: value, - ), - ); - }); - } - - QueryBuilder libItemIdBetween( - int lower, - int upper, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - BetweenCondition( - property: 0, - lower: lower, - upper: upper, - ), - ); - }); - } - - QueryBuilder speedIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const IsNullCondition(property: 1)); - }); - } - - QueryBuilder speedIsNotNull() { - return QueryBuilder.apply(not(), (query) { - return query.addFilterCondition(const IsNullCondition(property: 1)); - }); - } - - QueryBuilder speedEqualTo( - double? value, { - double epsilon = Filter.epsilon, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - EqualCondition( - property: 1, - value: value, - epsilon: epsilon, - ), - ); - }); - } - - QueryBuilder speedGreaterThan( - double? value, { - double epsilon = Filter.epsilon, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterCondition( - property: 1, - value: value, - epsilon: epsilon, - ), - ); - }); - } - - QueryBuilder - speedGreaterThanOrEqualTo( - double? value, { - double epsilon = Filter.epsilon, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - GreaterOrEqualCondition( - property: 1, - value: value, - epsilon: epsilon, - ), - ); - }); - } - - QueryBuilder speedLessThan( - double? value, { - double epsilon = Filter.epsilon, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessCondition( - property: 1, - value: value, - epsilon: epsilon, - ), - ); - }); - } - - QueryBuilder - speedLessThanOrEqualTo( - double? value, { - double epsilon = Filter.epsilon, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - LessOrEqualCondition( - property: 1, - value: value, - epsilon: epsilon, - ), - ); - }); - } - - QueryBuilder speedBetween( - double? lower, - double? upper, { - double epsilon = Filter.epsilon, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - BetweenCondition( - property: 1, - lower: lower, - upper: upper, - epsilon: epsilon, - ), - ); - }); - } -} - -extension BookPrefsQueryObject - on QueryBuilder {} - -extension BookPrefsQuerySortBy on QueryBuilder { - QueryBuilder sortByLibItemId() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(0); - }); - } - - QueryBuilder sortByLibItemIdDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(0, sort: Sort.desc); - }); - } - - QueryBuilder sortBySpeed() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(1); - }); - } - - QueryBuilder sortBySpeedDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(1, sort: Sort.desc); - }); - } -} - -extension BookPrefsQuerySortThenBy - on QueryBuilder { - QueryBuilder thenByLibItemId() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(0); - }); - } - - QueryBuilder thenByLibItemIdDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(0, sort: Sort.desc); - }); - } - - QueryBuilder thenBySpeed() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(1); - }); - } - - QueryBuilder thenBySpeedDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(1, sort: Sort.desc); - }); - } -} - -extension BookPrefsQueryWhereDistinct - on QueryBuilder { - QueryBuilder distinctBySpeed() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(1); - }); - } -} - -extension BookPrefsQueryProperty1 - on QueryBuilder { - QueryBuilder libItemIdProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(0); - }); - } - - QueryBuilder speedProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(1); - }); - } -} - -extension BookPrefsQueryProperty2 - on QueryBuilder { - QueryBuilder libItemIdProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(0); - }); - } - - QueryBuilder speedProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(1); - }); - } -} - -extension BookPrefsQueryProperty3 - on QueryBuilder { - QueryBuilder libItemIdProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(0); - }); - } - - QueryBuilder speedProperty() { - return QueryBuilder.apply(this, (query) { - return query.addProperty(1); - }); - } -} diff --git a/lib/db/register_models.dart b/lib/db/register_models.dart index e185e33..8695ab1 100644 --- a/lib/db/register_models.dart +++ b/lib/db/register_models.dart @@ -1,4 +1,4 @@ -import 'package:hive/hive.dart'; +import 'package:hive_plus_secure/hive_plus_secure.dart'; import 'package:vaani/features/per_book_settings/models/book_settings.dart'; import 'package:vaani/settings/models/models.dart'; diff --git a/lib/features/downloads/core/download_manager.dart b/lib/features/downloads/core/download_manager.dart index 5b6229d..e6db077 100644 --- a/lib/features/downloads/core/download_manager.dart +++ b/lib/features/downloads/core/download_manager.dart @@ -8,6 +8,7 @@ import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:vaani/shared/extensions/model_conversions.dart'; +import 'package:vaani/shared/extensions/obfuscation.dart'; final _logger = Logger('AudiobookDownloadManager'); final tq = MemoryTaskQueue(); @@ -35,7 +36,9 @@ class AudiobookDownloadManager { FileDownloader().addTaskQueue(tq); - _logger.fine('initialized with baseUrl: $baseUrl, token: $token'); + _logger.fine( + 'initialized with baseUrl: ${Uri.parse(baseUrl).obfuscate()} and token: ${token.obfuscate()}', + ); _logger.fine( 'requiresWiFi: $requiresWiFi, retries: $retries, allowPause: $allowPause', ); @@ -64,17 +67,13 @@ class AudiobookDownloadManager { late StreamSubscription _updatesSubscription; - Future queueAudioBookDownload( - LibraryItemExpanded item, - ) async { + Future queueAudioBookDownload(LibraryItemExpanded item) async { _logger.info('queuing download for item: ${item.id}'); // create a download task for each file in the item final directory = await getApplicationSupportDirectory(); for (final file in item.libraryFiles) { // check if the file is already downloaded - if (isFileDownloaded( - constructFilePath(directory, item, file), - )) { + if (isFileDownloaded(constructFilePath(directory, item, file))) { _logger.info('file already downloaded: ${file.metadata.filename}'); continue; } @@ -102,8 +101,7 @@ class AudiobookDownloadManager { Directory directory, LibraryItemExpanded item, LibraryFile file, - ) => - '${directory.path}/${item.relPath}/${file.metadata.filename}'; + ) => '${directory.path}/${item.relPath}/${file.metadata.filename}'; void dispose() { _updatesSubscription.cancel(); diff --git a/lib/features/downloads/providers/download_manager.dart b/lib/features/downloads/providers/download_manager.dart index 87a2191..7dba395 100644 --- a/lib/features/downloads/providers/download_manager.dart +++ b/lib/features/downloads/providers/download_manager.dart @@ -1,4 +1,5 @@ import 'package:background_downloader/background_downloader.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; @@ -31,9 +32,11 @@ class SimpleDownloadManager extends _$SimpleDownloadManager { core.tq.maxConcurrentByGroup = downloadSettings.maxConcurrentByGroup; ref.onDispose(() { + _logger.info('disposing download manager'); manager.dispose(); }); + _logger.config('initialized download manager'); return manager; } } @@ -49,15 +52,13 @@ class DownloadManager extends _$DownloadManager { return manager; } - Future queueAudioBookDownload( - LibraryItemExpanded item, - ) async { - await state.queueAudioBookDownload( - item, - ); + Future queueAudioBookDownload(LibraryItemExpanded item) async { + _logger.fine('queueing download for ${item.id}'); + await state.queueAudioBookDownload(item); } Future deleteDownloadedItem(LibraryItemExpanded item) async { + _logger.fine('deleting downloaded item ${item.id}'); await state.deleteDownloadedItem(item); ref.notifyListeners(); } @@ -78,58 +79,57 @@ class ItemDownloadProgress extends _$ItemDownloadProgress { Future build(String id) async { final item = await ref.watch(libraryItemProvider(id).future); final manager = ref.read(downloadManagerProvider); - manager.taskUpdateStream.map((taskUpdate) { - if (taskUpdate is! TaskProgressUpdate) { - return null; - } - if (taskUpdate.task.group == id) { - return taskUpdate; - } - }).listen((task) async { - if (task != null) { - final totalSize = item.totalSize; - // if total size is 0, return 0 - if (totalSize == 0) { - state = const AsyncValue.data(0.0); - return; - } - final downloadedFiles = await manager.getDownloadedFilesMetadata(item); - // calculate total size of downloaded files and total size of item, then divide - // to get percentage - final downloadedSize = downloadedFiles.fold( - 0, - (previousValue, element) => previousValue + element.metadata.size, - ); + manager.taskUpdateStream + .map((taskUpdate) { + if (taskUpdate is! TaskProgressUpdate) { + return null; + } + if (taskUpdate.task.group == id) { + return taskUpdate; + } + }) + .listen((task) async { + if (task != null) { + final totalSize = item.totalSize; + // if total size is 0, return 0 + if (totalSize == 0) { + state = const AsyncValue.data(0.0); + return; + } + final downloadedFiles = await manager.getDownloadedFilesMetadata( + item, + ); + // calculate total size of downloaded files and total size of item, then divide + // to get percentage + final downloadedSize = downloadedFiles.fold( + 0, + (previousValue, element) => previousValue + element.metadata.size, + ); - final inProgressFileSize = task.progress * task.expectedFileSize; - final totalDownloadedSize = downloadedSize + inProgressFileSize; - final progress = totalDownloadedSize / totalSize; - // if current progress is more than calculated progress, do not update - if (progress < (state.valueOrNull ?? 0.0)) { - return; - } + final inProgressFileSize = task.progress * task.expectedFileSize; + final totalDownloadedSize = downloadedSize + inProgressFileSize; + final progress = totalDownloadedSize / totalSize; + // if current progress is more than calculated progress, do not update + if (progress < (state.value ?? 0.0)) { + return; + } - state = AsyncValue.data(progress.clamp(0.0, 1.0)); - } - }); + state = AsyncValue.data(progress.clamp(0.0, 1.0)); + } + }); return null; } } @riverpod -FutureOr> downloadHistory( - DownloadHistoryRef ref, { - String? group, -}) async { +FutureOr> downloadHistory(Ref ref, {String? group}) async { return await FileDownloader().database.allRecords(group: group); } @riverpod class IsItemDownloaded extends _$IsItemDownloaded { @override - FutureOr build( - LibraryItemExpanded item, - ) { + FutureOr build(LibraryItemExpanded item) { final manager = ref.watch(downloadManagerProvider); return manager.isItemDownloaded(item); } diff --git a/lib/features/downloads/providers/download_manager.g.dart b/lib/features/downloads/providers/download_manager.g.dart index 43fc01c..0d0879e 100644 --- a/lib/features/downloads/providers/download_manager.g.dart +++ b/lib/features/downloads/providers/download_manager.g.dart @@ -6,619 +6,480 @@ part of 'download_manager.dart'; // RiverpodGenerator // ************************************************************************** -String _$downloadHistoryHash() => r'76c449e8abfa61d57566991686f534a06dc7fef7'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); +@ProviderFor(SimpleDownloadManager) +final simpleDownloadManagerProvider = SimpleDownloadManagerProvider._(); - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [downloadHistory]. -@ProviderFor(downloadHistory) -const downloadHistoryProvider = DownloadHistoryFamily(); - -/// See also [downloadHistory]. -class DownloadHistoryFamily extends Family>> { - /// See also [downloadHistory]. - const DownloadHistoryFamily(); - - /// See also [downloadHistory]. - DownloadHistoryProvider call({ - String? group, - }) { - return DownloadHistoryProvider( - group: group, - ); - } - - @override - DownloadHistoryProvider getProviderOverride( - covariant DownloadHistoryProvider provider, - ) { - return call( - group: provider.group, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'downloadHistoryProvider'; -} - -/// See also [downloadHistory]. -class DownloadHistoryProvider - extends AutoDisposeFutureProvider> { - /// See also [downloadHistory]. - DownloadHistoryProvider({ - String? group, - }) : this._internal( - (ref) => downloadHistory( - ref as DownloadHistoryRef, - group: group, - ), - from: downloadHistoryProvider, - name: r'downloadHistoryProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$downloadHistoryHash, - dependencies: DownloadHistoryFamily._dependencies, - allTransitiveDependencies: - DownloadHistoryFamily._allTransitiveDependencies, - group: group, - ); - - DownloadHistoryProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.group, - }) : super.internal(); - - final String? group; - - @override - Override overrideWith( - FutureOr> Function(DownloadHistoryRef provider) create, - ) { - return ProviderOverride( - origin: this, - override: DownloadHistoryProvider._internal( - (ref) => create(ref as DownloadHistoryRef), - from: from, - name: null, +final class SimpleDownloadManagerProvider + extends + $NotifierProvider< + SimpleDownloadManager, + core.AudiobookDownloadManager + > { + SimpleDownloadManagerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'simpleDownloadManagerProvider', + isAutoDispose: false, dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - group: group, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$simpleDownloadManagerHash(); + + @$internal + @override + SimpleDownloadManager create() => SimpleDownloadManager(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(core.AudiobookDownloadManager value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider( + value, ), ); } - - @override - AutoDisposeFutureProviderElement> createElement() { - return _DownloadHistoryProviderElement(this); - } - - @override - bool operator ==(Object other) { - return other is DownloadHistoryProvider && other.group == group; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, group.hashCode); - - return _SystemHash.finish(hash); - } -} - -mixin DownloadHistoryRef on AutoDisposeFutureProviderRef> { - /// The parameter `group` of this provider. - String? get group; -} - -class _DownloadHistoryProviderElement - extends AutoDisposeFutureProviderElement> - with DownloadHistoryRef { - _DownloadHistoryProviderElement(super.provider); - - @override - String? get group => (origin as DownloadHistoryProvider).group; } String _$simpleDownloadManagerHash() => - r'cec95717c86e422f88f78aa014d29e800e5a2089'; + r'8ab13f06ec5f2f73b73064bd285813dc890b7f36'; -/// See also [SimpleDownloadManager]. -@ProviderFor(SimpleDownloadManager) -final simpleDownloadManagerProvider = NotifierProvider.internal( - SimpleDownloadManager.new, - name: r'simpleDownloadManagerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$simpleDownloadManagerHash, - dependencies: null, - allTransitiveDependencies: null, -); +abstract class _$SimpleDownloadManager + extends $Notifier { + core.AudiobookDownloadManager build(); + @$mustCallSuper + @override + void runBuild() { + final ref = + this.ref + as $Ref< + core.AudiobookDownloadManager, + core.AudiobookDownloadManager + >; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier< + core.AudiobookDownloadManager, + core.AudiobookDownloadManager + >, + core.AudiobookDownloadManager, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} -typedef _$SimpleDownloadManager = Notifier; -String _$downloadManagerHash() => r'7296a39439230f77abbe7d3231dae748f09c7ecf'; - -/// See also [DownloadManager]. @ProviderFor(DownloadManager) -final downloadManagerProvider = - NotifierProvider.internal( - DownloadManager.new, - name: r'downloadManagerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$downloadManagerHash, - dependencies: null, - allTransitiveDependencies: null, -); +final downloadManagerProvider = DownloadManagerProvider._(); -typedef _$DownloadManager = Notifier; -String _$isItemDownloadingHash() => r'ea43c06393beec828134e08d5f896ddbcfbac8f0'; - -abstract class _$IsItemDownloading extends BuildlessAutoDisposeNotifier { - late final String id; - - bool build( - String id, - ); -} - -/// See also [IsItemDownloading]. -@ProviderFor(IsItemDownloading) -const isItemDownloadingProvider = IsItemDownloadingFamily(); - -/// See also [IsItemDownloading]. -class IsItemDownloadingFamily extends Family { - /// See also [IsItemDownloading]. - const IsItemDownloadingFamily(); - - /// See also [IsItemDownloading]. - IsItemDownloadingProvider call( - String id, - ) { - return IsItemDownloadingProvider( - id, - ); - } - - @override - IsItemDownloadingProvider getProviderOverride( - covariant IsItemDownloadingProvider provider, - ) { - return call( - provider.id, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'isItemDownloadingProvider'; -} - -/// See also [IsItemDownloading]. -class IsItemDownloadingProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [IsItemDownloading]. - IsItemDownloadingProvider( - String id, - ) : this._internal( - () => IsItemDownloading()..id = id, - from: isItemDownloadingProvider, - name: r'isItemDownloadingProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$isItemDownloadingHash, - dependencies: IsItemDownloadingFamily._dependencies, - allTransitiveDependencies: - IsItemDownloadingFamily._allTransitiveDependencies, - id: id, - ); - - IsItemDownloadingProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); - - final String id; - - @override - bool runNotifierBuild( - covariant IsItemDownloading notifier, - ) { - return notifier.build( - id, - ); - } - - @override - Override overrideWith(IsItemDownloading Function() create) { - return ProviderOverride( - origin: this, - override: IsItemDownloadingProvider._internal( - () => create()..id = id, - from: from, - name: null, +final class DownloadManagerProvider + extends $NotifierProvider { + DownloadManagerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'downloadManagerProvider', + isAutoDispose: false, dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$downloadManagerHash(); + + @$internal + @override + DownloadManager create() => DownloadManager(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(core.AudiobookDownloadManager value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider( + value, ), ); } +} + +String _$downloadManagerHash() => r'852012e32e613f86445afc7f7e4e85bec808e982'; + +abstract class _$DownloadManager + extends $Notifier { + core.AudiobookDownloadManager build(); + @$mustCallSuper + @override + void runBuild() { + final ref = + this.ref + as $Ref< + core.AudiobookDownloadManager, + core.AudiobookDownloadManager + >; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier< + core.AudiobookDownloadManager, + core.AudiobookDownloadManager + >, + core.AudiobookDownloadManager, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} + +@ProviderFor(IsItemDownloading) +final isItemDownloadingProvider = IsItemDownloadingFamily._(); + +final class IsItemDownloadingProvider + extends $NotifierProvider { + IsItemDownloadingProvider._({ + required IsItemDownloadingFamily super.from, + required String super.argument, + }) : super( + retry: null, + name: r'isItemDownloadingProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); @override - AutoDisposeNotifierProviderElement createElement() { - return _IsItemDownloadingProviderElement(this); + String debugGetCreateSourceHash() => _$isItemDownloadingHash(); + + @override + String toString() { + return r'isItemDownloadingProvider' + '' + '($argument)'; + } + + @$internal + @override + IsItemDownloading create() => IsItemDownloading(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(bool value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); } @override bool operator ==(Object other) { - return other is IsItemDownloadingProvider && other.id == id; + return other is IsItemDownloadingProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -mixin IsItemDownloadingRef on AutoDisposeNotifierProviderRef { - /// The parameter `id` of this provider. - String get id; -} +String _$isItemDownloadingHash() => r'ea43c06393beec828134e08d5f896ddbcfbac8f0'; -class _IsItemDownloadingProviderElement - extends AutoDisposeNotifierProviderElement - with IsItemDownloadingRef { - _IsItemDownloadingProviderElement(super.provider); +final class IsItemDownloadingFamily extends $Family + with $ClassFamilyOverride { + IsItemDownloadingFamily._() + : super( + retry: null, + name: r'isItemDownloadingProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + IsItemDownloadingProvider call(String id) => + IsItemDownloadingProvider._(argument: id, from: this); @override - String get id => (origin as IsItemDownloadingProvider).id; + String toString() => r'isItemDownloadingProvider'; +} + +abstract class _$IsItemDownloading extends $Notifier { + late final _$args = ref.$arg as String; + String get id => _$args; + + bool build(String id); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + bool, + Object?, + Object? + >; + element.handleCreate(ref, () => build(_$args)); + } +} + +@ProviderFor(ItemDownloadProgress) +final itemDownloadProgressProvider = ItemDownloadProgressFamily._(); + +final class ItemDownloadProgressProvider + extends $AsyncNotifierProvider { + ItemDownloadProgressProvider._({ + required ItemDownloadProgressFamily super.from, + required String super.argument, + }) : super( + retry: null, + name: r'itemDownloadProgressProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$itemDownloadProgressHash(); + + @override + String toString() { + return r'itemDownloadProgressProvider' + '' + '($argument)'; + } + + @$internal + @override + ItemDownloadProgress create() => ItemDownloadProgress(); + + @override + bool operator ==(Object other) { + return other is ItemDownloadProgressProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } } String _$itemDownloadProgressHash() => - r'd007c55c6e2e4b992069d0306df8a600225d8598'; + r'95f6ec0945f73d9156bf89bdb1865f3b2c9ffcaa'; -abstract class _$ItemDownloadProgress - extends BuildlessAutoDisposeAsyncNotifier { - late final String id; - - FutureOr build( - String id, - ); -} - -/// See also [ItemDownloadProgress]. -@ProviderFor(ItemDownloadProgress) -const itemDownloadProgressProvider = ItemDownloadProgressFamily(); - -/// See also [ItemDownloadProgress]. -class ItemDownloadProgressFamily extends Family> { - /// See also [ItemDownloadProgress]. - const ItemDownloadProgressFamily(); - - /// See also [ItemDownloadProgress]. - ItemDownloadProgressProvider call( - String id, - ) { - return ItemDownloadProgressProvider( - id, - ); - } - - @override - ItemDownloadProgressProvider getProviderOverride( - covariant ItemDownloadProgressProvider provider, - ) { - return call( - provider.id, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'itemDownloadProgressProvider'; -} - -/// See also [ItemDownloadProgress]. -class ItemDownloadProgressProvider extends AutoDisposeAsyncNotifierProviderImpl< - ItemDownloadProgress, double?> { - /// See also [ItemDownloadProgress]. - ItemDownloadProgressProvider( - String id, - ) : this._internal( - () => ItemDownloadProgress()..id = id, - from: itemDownloadProgressProvider, - name: r'itemDownloadProgressProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$itemDownloadProgressHash, - dependencies: ItemDownloadProgressFamily._dependencies, - allTransitiveDependencies: - ItemDownloadProgressFamily._allTransitiveDependencies, - id: id, - ); - - ItemDownloadProgressProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); - - final String id; - - @override - FutureOr runNotifierBuild( - covariant ItemDownloadProgress notifier, - ) { - return notifier.build( - id, - ); - } - - @override - Override overrideWith(ItemDownloadProgress Function() create) { - return ProviderOverride( - origin: this, - override: ItemDownloadProgressProvider._internal( - () => create()..id = id, - from: from, - name: null, +final class ItemDownloadProgressFamily extends $Family + with + $ClassFamilyOverride< + ItemDownloadProgress, + AsyncValue, + double?, + FutureOr, + String + > { + ItemDownloadProgressFamily._() + : super( + retry: null, + name: r'itemDownloadProgressProvider', dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), - ); - } + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + ItemDownloadProgressProvider call(String id) => + ItemDownloadProgressProvider._(argument: id, from: this); @override - AutoDisposeAsyncNotifierProviderElement - createElement() { - return _ItemDownloadProgressProviderElement(this); + String toString() => r'itemDownloadProgressProvider'; +} + +abstract class _$ItemDownloadProgress extends $AsyncNotifier { + late final _$args = ref.$arg as String; + String get id => _$args; + + FutureOr build(String id); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref, double?>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, double?>, + AsyncValue, + Object?, + Object? + >; + element.handleCreate(ref, () => build(_$args)); + } +} + +@ProviderFor(downloadHistory) +final downloadHistoryProvider = DownloadHistoryFamily._(); + +final class DownloadHistoryProvider + extends + $FunctionalProvider< + AsyncValue>, + List, + FutureOr> + > + with $FutureModifier>, $FutureProvider> { + DownloadHistoryProvider._({ + required DownloadHistoryFamily super.from, + required String? super.argument, + }) : super( + retry: null, + name: r'downloadHistoryProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$downloadHistoryHash(); + + @override + String toString() { + return r'downloadHistoryProvider' + '' + '($argument)'; + } + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr> create(Ref ref) { + final argument = this.argument as String?; + return downloadHistory(ref, group: argument); } @override bool operator ==(Object other) { - return other is ItemDownloadProgressProvider && other.id == id; + return other is DownloadHistoryProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -mixin ItemDownloadProgressRef on AutoDisposeAsyncNotifierProviderRef { - /// The parameter `id` of this provider. - String get id; -} +String _$downloadHistoryHash() => r'4d8b84e30f7ff5ae69d23c8e03ff24af1234a1ad'; -class _ItemDownloadProgressProviderElement - extends AutoDisposeAsyncNotifierProviderElement with ItemDownloadProgressRef { - _ItemDownloadProgressProviderElement(super.provider); +final class DownloadHistoryFamily extends $Family + with $FunctionalFamilyOverride>, String?> { + DownloadHistoryFamily._() + : super( + retry: null, + name: r'downloadHistoryProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + DownloadHistoryProvider call({String? group}) => + DownloadHistoryProvider._(argument: group, from: this); @override - String get id => (origin as ItemDownloadProgressProvider).id; + String toString() => r'downloadHistoryProvider'; +} + +@ProviderFor(IsItemDownloaded) +final isItemDownloadedProvider = IsItemDownloadedFamily._(); + +final class IsItemDownloadedProvider + extends $AsyncNotifierProvider { + IsItemDownloadedProvider._({ + required IsItemDownloadedFamily super.from, + required LibraryItemExpanded super.argument, + }) : super( + retry: null, + name: r'isItemDownloadedProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$isItemDownloadedHash(); + + @override + String toString() { + return r'isItemDownloadedProvider' + '' + '($argument)'; + } + + @$internal + @override + IsItemDownloaded create() => IsItemDownloaded(); + + @override + bool operator ==(Object other) { + return other is IsItemDownloadedProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } } String _$isItemDownloadedHash() => r'9bb7ba28bdb73e1ba706e849fedc9c7bd67f4b67'; -abstract class _$IsItemDownloaded - extends BuildlessAutoDisposeAsyncNotifier { - late final LibraryItemExpanded item; - - FutureOr build( - LibraryItemExpanded item, - ); -} - -/// See also [IsItemDownloaded]. -@ProviderFor(IsItemDownloaded) -const isItemDownloadedProvider = IsItemDownloadedFamily(); - -/// See also [IsItemDownloaded]. -class IsItemDownloadedFamily extends Family> { - /// See also [IsItemDownloaded]. - const IsItemDownloadedFamily(); - - /// See also [IsItemDownloaded]. - IsItemDownloadedProvider call( - LibraryItemExpanded item, - ) { - return IsItemDownloadedProvider( - item, - ); - } - - @override - IsItemDownloadedProvider getProviderOverride( - covariant IsItemDownloadedProvider provider, - ) { - return call( - provider.item, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'isItemDownloadedProvider'; -} - -/// See also [IsItemDownloaded]. -class IsItemDownloadedProvider - extends AutoDisposeAsyncNotifierProviderImpl { - /// See also [IsItemDownloaded]. - IsItemDownloadedProvider( - LibraryItemExpanded item, - ) : this._internal( - () => IsItemDownloaded()..item = item, - from: isItemDownloadedProvider, - name: r'isItemDownloadedProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$isItemDownloadedHash, - dependencies: IsItemDownloadedFamily._dependencies, - allTransitiveDependencies: - IsItemDownloadedFamily._allTransitiveDependencies, - item: item, - ); - - IsItemDownloadedProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.item, - }) : super.internal(); - - final LibraryItemExpanded item; - - @override - FutureOr runNotifierBuild( - covariant IsItemDownloaded notifier, - ) { - return notifier.build( - item, - ); - } - - @override - Override overrideWith(IsItemDownloaded Function() create) { - return ProviderOverride( - origin: this, - override: IsItemDownloadedProvider._internal( - () => create()..item = item, - from: from, - name: null, +final class IsItemDownloadedFamily extends $Family + with + $ClassFamilyOverride< + IsItemDownloaded, + AsyncValue, + bool, + FutureOr, + LibraryItemExpanded + > { + IsItemDownloadedFamily._() + : super( + retry: null, + name: r'isItemDownloadedProvider', dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - item: item, - ), - ); - } + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + IsItemDownloadedProvider call(LibraryItemExpanded item) => + IsItemDownloadedProvider._(argument: item, from: this); @override - AutoDisposeAsyncNotifierProviderElement - createElement() { - return _IsItemDownloadedProviderElement(this); - } - - @override - bool operator ==(Object other) { - return other is IsItemDownloadedProvider && other.item == item; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, item.hashCode); - - return _SystemHash.finish(hash); - } + String toString() => r'isItemDownloadedProvider'; } -mixin IsItemDownloadedRef on AutoDisposeAsyncNotifierProviderRef { - /// The parameter `item` of this provider. - LibraryItemExpanded get item; -} - -class _IsItemDownloadedProviderElement - extends AutoDisposeAsyncNotifierProviderElement - with IsItemDownloadedRef { - _IsItemDownloadedProviderElement(super.provider); +abstract class _$IsItemDownloaded extends $AsyncNotifier { + late final _$args = ref.$arg as LibraryItemExpanded; + LibraryItemExpanded get item => _$args; + FutureOr build(LibraryItemExpanded item); + @$mustCallSuper @override - LibraryItemExpanded get item => (origin as IsItemDownloadedProvider).item; + void runBuild() { + final ref = this.ref as $Ref, bool>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, bool>, + AsyncValue, + Object?, + Object? + >; + element.handleCreate(ref, () => build(_$args)); + } } -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/features/downloads/view/downloads_page.dart b/lib/features/downloads/view/downloads_page.dart index e8c0e9c..7d2b2cb 100644 --- a/lib/features/downloads/view/downloads_page.dart +++ b/lib/features/downloads/view/downloads_page.dart @@ -11,10 +11,7 @@ class DownloadsPage extends HookConsumerWidget { final downloadHistory = ref.watch(downloadHistoryProvider()); return Scaffold( - appBar: AppBar( - title: const Text('Downloads'), - backgroundColor: Colors.transparent, - ), + appBar: AppBar(title: const Text('Downloads')), body: Center( // history of downloads child: downloadHistory.when( diff --git a/lib/features/explore/providers/search_controller.g.dart b/lib/features/explore/providers/search_controller.g.dart index 4e6fa28..a44d078 100644 --- a/lib/features/explore/providers/search_controller.g.dart +++ b/lib/features/explore/providers/search_controller.g.dart @@ -6,24 +6,64 @@ part of 'search_controller.dart'; // RiverpodGenerator // ************************************************************************** +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// The controller for the search bar. + +@ProviderFor(GlobalSearchController) +final globalSearchControllerProvider = GlobalSearchControllerProvider._(); + +/// The controller for the search bar. +final class GlobalSearchControllerProvider + extends $NotifierProvider> { + /// The controller for the search bar. + GlobalSearchControllerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'globalSearchControllerProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$globalSearchControllerHash(); + + @$internal + @override + GlobalSearchController create() => GlobalSearchController(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider>(value), + ); + } +} + String _$globalSearchControllerHash() => r'd854ace6f2e00a10fc33aba63051375f82ad1b10'; /// The controller for the search bar. -/// -/// Copied from [GlobalSearchController]. -@ProviderFor(GlobalSearchController) -final globalSearchControllerProvider = - NotifierProvider>.internal( - GlobalSearchController.new, - name: r'globalSearchControllerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$globalSearchControllerHash, - dependencies: null, - allTransitiveDependencies: null, -); -typedef _$GlobalSearchController = Notifier>; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +abstract class _$GlobalSearchController + extends $Notifier> { + Raw build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref, Raw>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, Raw>, + Raw, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/features/explore/providers/search_result_provider.dart b/lib/features/explore/providers/search_result_provider.dart index 1552a69..2c903f7 100644 --- a/lib/features/explore/providers/search_result_provider.dart +++ b/lib/features/explore/providers/search_result_provider.dart @@ -1,3 +1,4 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:vaani/api/api_provider.dart'; @@ -8,7 +9,7 @@ part 'search_result_provider.g.dart'; /// The provider for the search result. @riverpod FutureOr searchResult( - SearchResultRef ref, + Ref ref, String query, { int limit = 25, }) async { diff --git a/lib/features/explore/providers/search_result_provider.g.dart b/lib/features/explore/providers/search_result_provider.g.dart index 20d8c40..51b077b 100644 --- a/lib/features/explore/providers/search_result_provider.g.dart +++ b/lib/features/explore/providers/search_result_provider.g.dart @@ -6,184 +6,94 @@ part of 'search_result_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$searchResultHash() => r'9baa643cce24f3a5e022f42202e423373939ef95'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning /// The provider for the search result. -/// -/// Copied from [searchResult]. + @ProviderFor(searchResult) -const searchResultProvider = SearchResultFamily(); +final searchResultProvider = SearchResultFamily._(); /// The provider for the search result. -/// -/// Copied from [searchResult]. -class SearchResultFamily extends Family> { - /// The provider for the search result. - /// - /// Copied from [searchResult]. - const SearchResultFamily(); +final class SearchResultProvider + extends + $FunctionalProvider< + AsyncValue, + LibrarySearchResponse?, + FutureOr + > + with + $FutureModifier, + $FutureProvider { /// The provider for the search result. - /// - /// Copied from [searchResult]. - SearchResultProvider call( - String query, { - int limit = 25, - }) { - return SearchResultProvider( - query, - limit: limit, - ); + SearchResultProvider._({ + required SearchResultFamily super.from, + required (String, {int limit}) super.argument, + }) : super( + retry: null, + name: r'searchResultProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$searchResultHash(); + + @override + String toString() { + return r'searchResultProvider' + '' + '$argument'; } + @$internal @override - SearchResultProvider getProviderOverride( - covariant SearchResultProvider provider, - ) { - return call( - provider.query, - limit: provider.limit, - ); - } - - static const Iterable? _dependencies = null; + $FutureProviderElement $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'searchResultProvider'; -} - -/// The provider for the search result. -/// -/// Copied from [searchResult]. -class SearchResultProvider - extends AutoDisposeFutureProvider { - /// The provider for the search result. - /// - /// Copied from [searchResult]. - SearchResultProvider( - String query, { - int limit = 25, - }) : this._internal( - (ref) => searchResult( - ref as SearchResultRef, - query, - limit: limit, - ), - from: searchResultProvider, - name: r'searchResultProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$searchResultHash, - dependencies: SearchResultFamily._dependencies, - allTransitiveDependencies: - SearchResultFamily._allTransitiveDependencies, - query: query, - limit: limit, - ); - - SearchResultProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.query, - required this.limit, - }) : super.internal(); - - final String query; - final int limit; - - @override - Override overrideWith( - FutureOr Function(SearchResultRef provider) create, - ) { - return ProviderOverride( - origin: this, - override: SearchResultProvider._internal( - (ref) => create(ref as SearchResultRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - query: query, - limit: limit, - ), - ); - } - - @override - AutoDisposeFutureProviderElement createElement() { - return _SearchResultProviderElement(this); + FutureOr create(Ref ref) { + final argument = this.argument as (String, {int limit}); + return searchResult(ref, argument.$1, limit: argument.limit); } @override bool operator ==(Object other) { - return other is SearchResultProvider && - other.query == query && - other.limit == limit; + return other is SearchResultProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, query.hashCode); - hash = _SystemHash.combine(hash, limit.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -mixin SearchResultRef on AutoDisposeFutureProviderRef { - /// The parameter `query` of this provider. - String get query; +String _$searchResultHash() => r'33785de298ad0d53c9d21e8fec88ba2f22f1363f'; - /// The parameter `limit` of this provider. - int get limit; -} +/// The provider for the search result. -class _SearchResultProviderElement - extends AutoDisposeFutureProviderElement - with SearchResultRef { - _SearchResultProviderElement(super.provider); +final class SearchResultFamily extends $Family + with + $FunctionalFamilyOverride< + FutureOr, + (String, {int limit}) + > { + SearchResultFamily._() + : super( + retry: null, + name: r'searchResultProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + /// The provider for the search result. + + SearchResultProvider call(String query, {int limit = 25}) => + SearchResultProvider._(argument: (query, limit: limit), from: this); @override - String get query => (origin as SearchResultProvider).query; - @override - int get limit => (origin as SearchResultProvider).limit; + String toString() => r'searchResultProvider'; } -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/features/explore/view/explore_page.dart b/lib/features/explore/view/explore_page.dart index 2a46983..e747d26 100644 --- a/lib/features/explore/view/explore_page.dart +++ b/lib/features/explore/view/explore_page.dart @@ -28,19 +28,14 @@ class ExplorePage extends HookConsumerWidget { final settings = ref.watch(appSettingsProvider); final api = ref.watch(authenticatedApiProvider); return Scaffold( - appBar: AppBar( - title: const Text('Explore'), - backgroundColor: Colors.transparent, - ), + appBar: AppBar(title: const Text('Explore')), body: const MySearchBar(), ); } } class MySearchBar extends HookConsumerWidget { - const MySearchBar({ - super.key, - }); + const MySearchBar({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -62,8 +57,11 @@ class MySearchBar extends HookConsumerWidget { currentQuery = query; // In a real application, there should be some error handling here. - final options = await api.libraries - .search(libraryId: settings.activeLibraryId!, query: query, limit: 3); + final options = await api.libraries.search( + libraryId: settings.activeLibraryId!, + query: query, + limit: 3, + ); // If another search happened after this one, throw away these options. if (currentQuery != query) { @@ -98,9 +96,10 @@ class MySearchBar extends HookConsumerWidget { // opacity: 0.5 for the hint text hintStyle: WidgetStatePropertyAll( Theme.of(context).textTheme.bodyMedium!.copyWith( - color: - Theme.of(context).colorScheme.onSurface.withOpacity(0.5), - ), + color: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.5), + ), ), textInputAction: TextInputAction.search, onTapOutside: (_) { @@ -119,12 +118,7 @@ class MySearchBar extends HookConsumerWidget { ); }, viewOnSubmitted: (value) { - context.pushNamed( - Routes.search.name, - queryParameters: { - 'q': value, - }, - ); + context.pushNamed(Routes.search.name, queryParameters: {'q': value}); }, suggestionsBuilder: (context, controller) async { // check if the search controller is empty @@ -190,14 +184,12 @@ List buildBookSearchResult( SearchResultMiniSection( // title: 'Books', category: SearchResultCategory.books, - options: options.book.map( - (result) { - // convert result to a book object - final book = result.libraryItem.media.asBookExpanded; - final metadata = book.metadata.asBookMetadataExpanded; - return BookSearchResultMini(book: book, metadata: metadata); - }, - ), + options: options.book.map((result) { + // convert result to a book object + final book = result.libraryItem.media.asBookExpanded; + final metadata = book.metadata.asBookMetadataExpanded; + return BookSearchResultMini(book: book, metadata: metadata); + }), ), ); } @@ -206,11 +198,9 @@ List buildBookSearchResult( SearchResultMiniSection( // title: 'Authors', category: SearchResultCategory.authors, - options: options.authors.map( - (result) { - return ListTile(title: Text(result.name)); - }, - ), + options: options.authors.map((result) { + return ListTile(title: Text(result.name)); + }), ), ); } @@ -231,7 +221,7 @@ class BookSearchResultMini extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final item = ref.watch(libraryItemProvider(book.libraryItemId)).valueOrNull; + final item = ref.watch(libraryItemProvider(book.libraryItemId)).value; final image = item == null ? const AsyncValue.loading() : ref.watch(coverImageProvider(item.id)); @@ -244,10 +234,7 @@ class BookSearchResultMini extends HookConsumerWidget { child: ClipRRect( borderRadius: BorderRadius.circular(5), child: image.when( - data: (bytes) => Image.memory( - bytes, - fit: BoxFit.cover, - ), + data: (bytes) => Image.memory(bytes, fit: BoxFit.cover), loading: () => const BookCoverSkeleton(), error: (error, _) => const Icon(Icons.error), ), @@ -258,11 +245,7 @@ class BookSearchResultMini extends HookConsumerWidget { subtitle: Text( maxLines: 1, overflow: TextOverflow.ellipsis, - metadata.authors - .map( - (author) => author.name, - ) - .join(', '), + metadata.authors.map((author) => author.name).join(', '), ), onTap: () { // navigate to the book details page diff --git a/lib/features/explore/view/search_result_page.dart b/lib/features/explore/view/search_result_page.dart index 10a8a39..fef097e 100644 --- a/lib/features/explore/view/search_result_page.dart +++ b/lib/features/explore/view/search_result_page.dart @@ -5,13 +5,7 @@ import 'package:vaani/features/explore/providers/search_result_provider.dart'; import 'package:vaani/features/explore/view/explore_page.dart'; import 'package:vaani/shared/extensions/model_conversions.dart'; -enum SearchResultCategory { - books, - authors, - series, - tags, - narrators, -} +enum SearchResultCategory { books, authors, series, tags, narrators } class SearchResultPage extends HookConsumerWidget { const SearchResultPage({ @@ -41,9 +35,7 @@ class SearchResultPage extends HookConsumerWidget { body: results.when( data: (options) { if (options == null) { - return Container( - child: const Text('No data found'), - ); + return Container(child: const Text('No data found')); } if (options is BookLibrarySearchResponse) { if (category == null) { @@ -51,18 +43,15 @@ class SearchResultPage extends HookConsumerWidget { } return switch (category!) { SearchResultCategory.books => ListView.builder( - itemCount: options.book.length, - itemBuilder: (context, index) { - final book = - options.book[index].libraryItem.media.asBookExpanded; - final metadata = book.metadata.asBookMetadataExpanded; + itemCount: options.book.length, + itemBuilder: (context, index) { + final book = + options.book[index].libraryItem.media.asBookExpanded; + final metadata = book.metadata.asBookMetadataExpanded; - return BookSearchResultMini( - book: book, - metadata: metadata, - ); - }, - ), + return BookSearchResultMini(book: book, metadata: metadata); + }, + ), SearchResultCategory.authors => Container(), SearchResultCategory.series => Container(), SearchResultCategory.tags => Container(), @@ -71,12 +60,8 @@ class SearchResultPage extends HookConsumerWidget { } return null; }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), - error: (error, stackTrace) => Center( - child: Text('Error: $error'), - ), + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stackTrace) => Center(child: Text('Error: $error')), ), ); } diff --git a/lib/features/item_viewer/view/library_item_actions.dart b/lib/features/item_viewer/view/library_item_actions.dart index f1eec3d..50ef9c2 100644 --- a/lib/features/item_viewer/view/library_item_actions.dart +++ b/lib/features/item_viewer/view/library_item_actions.dart @@ -26,16 +26,13 @@ import 'package:vaani/shared/extensions/model_conversions.dart'; import 'package:vaani/shared/utils.dart'; class LibraryItemActions extends HookConsumerWidget { - const LibraryItemActions({ - super.key, - required this.id, - }); + const LibraryItemActions({super.key, required this.id}); final String id; @override Widget build(BuildContext context, WidgetRef ref) { - final item = ref.watch(libraryItemProvider(id)).valueOrNull; + final item = ref.watch(libraryItemProvider(id)).value; if (item == null) { return const SizedBox.shrink(); } @@ -68,9 +65,7 @@ class LibraryItemActions extends HookConsumerWidget { // read list button IconButton( onPressed: () {}, - icon: const Icon( - Icons.playlist_add_rounded, - ), + icon: const Icon(Icons.playlist_add_rounded), ), // share button IconButton( @@ -79,8 +74,9 @@ class LibraryItemActions extends HookConsumerWidget { var currentServerUrl = apiSettings.activeServer!.serverUrl; if (!currentServerUrl.hasScheme) { - currentServerUrl = - Uri.https(currentServerUrl.toString()); + currentServerUrl = Uri.https( + currentServerUrl.toString(), + ); } handleLaunchUrl( Uri.parse( @@ -140,7 +136,8 @@ class LibraryItemActions extends HookConsumerWidget { .database .deleteRecordWithId( record - .task.taskId, + .task + .taskId, ); Navigator.pop(context); }, @@ -161,8 +158,8 @@ class LibraryItemActions extends HookConsumerWidget { // open the file location final didOpen = await FileDownloader().openFile( - task: record.task, - ); + task: record.task, + ); if (!didOpen) { appLogger.warning( @@ -182,16 +179,13 @@ class LibraryItemActions extends HookConsumerWidget { loading: () => const Center( child: CircularProgressIndicator(), ), - error: (error, stackTrace) => Center( - child: Text('Error: $error'), - ), + error: (error, stackTrace) => + Center(child: Text('Error: $error')), ); }, ); }, - icon: const Icon( - Icons.more_vert_rounded, - ), + icon: const Icon(Icons.more_vert_rounded), ), ], ), @@ -206,25 +200,20 @@ class LibraryItemActions extends HookConsumerWidget { } class LibItemDownloadButton extends HookConsumerWidget { - const LibItemDownloadButton({ - super.key, - required this.item, - }); + const LibItemDownloadButton({super.key, required this.item}); final shelfsdk.LibraryItemExpanded item; @override Widget build(BuildContext context, WidgetRef ref) { final isItemDownloaded = ref.watch(isItemDownloadedProvider(item)); - if (isItemDownloaded.valueOrNull ?? false) { + if (isItemDownloaded.value ?? false) { return AlreadyItemDownloadedButton(item: item); } final isItemDownloading = ref.watch(isItemDownloadingProvider(item.id)); return isItemDownloading - ? ItemCurrentlyInDownloadQueue( - item: item, - ) + ? ItemCurrentlyInDownloadQueue(item: item) : IconButton( onPressed: () { appLogger.fine('Pressed download button'); @@ -233,18 +222,13 @@ class LibItemDownloadButton extends HookConsumerWidget { .read(downloadManagerProvider.notifier) .queueAudioBookDownload(item); }, - icon: const Icon( - Icons.download_rounded, - ), + icon: const Icon(Icons.download_rounded), ); } } class ItemCurrentlyInDownloadQueue extends HookConsumerWidget { - const ItemCurrentlyInDownloadQueue({ - super.key, - required this.item, - }); + const ItemCurrentlyInDownloadQueue({super.key, required this.item}); final shelfsdk.LibraryItemExpanded item; @@ -252,7 +236,7 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final progress = ref .watch(itemDownloadProgressProvider(item.id)) - .valueOrNull + .value ?.clamp(0.05, 1.0); if (progress == 1) { @@ -263,17 +247,12 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget { return Stack( alignment: Alignment.center, children: [ - CircularProgressIndicator( - value: progress, - strokeWidth: 2, - ), + CircularProgressIndicator(value: progress, strokeWidth: 2), const Icon( - Icons.download, - // color: Theme.of(context).progressIndicatorTheme.color, - ) - .animate( - onPlay: (controller) => controller.repeat(), + Icons.download, + // color: Theme.of(context).progressIndicatorTheme.color, ) + .animate(onPlay: (controller) => controller.repeat()) .fade( duration: shimmerDuration, end: 1, @@ -292,10 +271,7 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget { } class AlreadyItemDownloadedButton extends HookConsumerWidget { - const AlreadyItemDownloadedButton({ - super.key, - required this.item, - }); + const AlreadyItemDownloadedButton({super.key, required this.item}); final shelfsdk.LibraryItemExpanded item; @@ -317,25 +293,18 @@ class AlreadyItemDownloadedButton extends HookConsumerWidget { top: 8.0, bottom: (isBookPlaying ? playerMinHeight : 0) + 8, ), - child: DownloadSheet( - item: item, - ), + child: DownloadSheet(item: item), ); }, ); }, - icon: const Icon( - Icons.download_done_rounded, - ), + icon: const Icon(Icons.download_done_rounded), ); } } class DownloadSheet extends HookConsumerWidget { - const DownloadSheet({ - super.key, - required this.item, - }); + const DownloadSheet({super.key, required this.item}); final shelfsdk.LibraryItemExpanded item; @@ -367,9 +336,7 @@ class DownloadSheet extends HookConsumerWidget { // ), ListTile( title: const Text('Delete'), - leading: const Icon( - Icons.delete_rounded, - ), + leading: const Icon(Icons.delete_rounded), onTap: () async { // show the delete dialog final wasDeleted = await showDialog( @@ -387,9 +354,7 @@ class DownloadSheet extends HookConsumerWidget { // delete the file ref .read(downloadManagerProvider.notifier) - .deleteDownloadedItem( - item, - ); + .deleteDownloadedItem(item); GoRouter.of(context).pop(true); }, child: const Text('Yes'), @@ -409,11 +374,7 @@ class DownloadSheet extends HookConsumerWidget { appLogger.fine('Deleted ${item.media.metadata.title}'); GoRouter.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Deleted ${item.media.metadata.title}', - ), - ), + SnackBar(content: Text('Deleted ${item.media.metadata.title}')), ); } }, @@ -424,10 +385,7 @@ class DownloadSheet extends HookConsumerWidget { } class _LibraryItemPlayButton extends HookConsumerWidget { - const _LibraryItemPlayButton({ - super.key, - required this.item, - }); + const _LibraryItemPlayButton({required this.item}); final shelfsdk.LibraryItemExpanded item; @@ -478,9 +436,7 @@ class _LibraryItemPlayButton extends HookConsumerWidget { ), label: Text(getPlayDisplayText()), style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), ), ); } @@ -503,11 +459,11 @@ class DynamicItemPlayIcon extends StatelessWidget { return Icon( isCurrentBookSetInPlayer ? isPlayingThisBook - ? Icons.pause_rounded - : Icons.play_arrow_rounded + ? Icons.pause_rounded + : Icons.play_arrow_rounded : isBookCompleted - ? Icons.replay_rounded - : Icons.play_arrow_rounded, + ? Icons.replay_rounded + : Icons.play_arrow_rounded, ); } } @@ -518,7 +474,7 @@ Future libraryItemPlayButtonOnPressed({ required shelfsdk.BookExpanded book, shelfsdk.MediaProgress? userMediaProgress, }) async { - debugPrint('Pressed play/resume button'); + appLogger.info('Pressed play/resume button'); final player = ref.watch(audiobookPlayerProvider); final isCurrentBookSetInPlayer = player.book == book; @@ -527,11 +483,12 @@ Future libraryItemPlayButtonOnPressed({ Future? setSourceFuture; // set the book to the player if not already set if (!isCurrentBookSetInPlayer) { - debugPrint('Setting the book ${book.libraryItemId}'); - debugPrint('Initial position: ${userMediaProgress?.currentTime}'); + appLogger.info('Setting the book ${book.libraryItemId}'); + appLogger.info('Initial position: ${userMediaProgress?.currentTime}'); final downloadManager = ref.watch(simpleDownloadManagerProvider); - final libItem = - await ref.read(libraryItemProvider(book.libraryItemId).future); + final libItem = await ref.read( + libraryItemProvider(book.libraryItemId).future, + ); final downloadedUris = await downloadManager.getDownloadedFilesUri(libItem); setSourceFuture = player.setSourceAudiobook( book, @@ -539,16 +496,17 @@ Future libraryItemPlayButtonOnPressed({ downloadedUris: downloadedUris, ); } else { - debugPrint('Book was already set'); + appLogger.info('Book was already set'); if (isPlayingThisBook) { - debugPrint('Pausing the book'); + appLogger.info('Pausing the book'); await player.pause(); return; } } // set the volume as this is the first time playing and dismissing causes the volume to go to 0 - var bookPlayerSettings = - ref.read(bookSettingsProvider(book.libraryItemId)).playerSettings; + var bookPlayerSettings = ref + .read(bookSettingsProvider(book.libraryItemId)) + .playerSettings; var appPlayerSettings = ref.read(appSettingsProvider).playerSettings; var configurePlayerForEveryBook = @@ -560,14 +518,14 @@ Future libraryItemPlayButtonOnPressed({ player.setVolume( configurePlayerForEveryBook ? bookPlayerSettings.preferredDefaultVolume ?? - appPlayerSettings.preferredDefaultVolume + appPlayerSettings.preferredDefaultVolume : appPlayerSettings.preferredDefaultVolume, ), // set the speed player.setSpeed( configurePlayerForEveryBook ? bookPlayerSettings.preferredDefaultSpeed ?? - appPlayerSettings.preferredDefaultSpeed + appPlayerSettings.preferredDefaultSpeed : appPlayerSettings.preferredDefaultSpeed, ), ]); diff --git a/lib/features/item_viewer/view/library_item_hero_section.dart b/lib/features/item_viewer/view/library_item_hero_section.dart index ea21da6..a103b7a 100644 --- a/lib/features/item_viewer/view/library_item_hero_section.dart +++ b/lib/features/item_viewer/view/library_item_hero_section.dart @@ -15,7 +15,7 @@ import 'package:vaani/settings/app_settings_provider.dart'; import 'package:vaani/shared/extensions/duration_format.dart'; import 'package:vaani/shared/extensions/model_conversions.dart'; import 'package:vaani/shared/widgets/shelves/book_shelf.dart'; -import 'package:vaani/theme/theme_from_cover_provider.dart'; +import 'package:vaani/theme/providers/theme_from_cover_provider.dart'; class LibraryItemHeroSection extends HookConsumerWidget { const LibraryItemHeroSection({ @@ -42,14 +42,13 @@ class LibraryItemHeroSection extends HookConsumerWidget { child: Column( children: [ Hero( - tag: HeroTagPrefixes.bookCover + + tag: + HeroTagPrefixes.bookCover + itemId + (extraMap?.heroTagSuffix ?? ''), child: ClipRRect( borderRadius: BorderRadius.circular(16), - child: _BookCover( - itemId: itemId, - ), + child: _BookCover(itemId: itemId), ), ), // a progress bar @@ -59,9 +58,7 @@ class LibraryItemHeroSection extends HookConsumerWidget { right: 8.0, left: 8.0, ), - child: _LibraryItemProgressIndicator( - id: itemId, - ), + child: _LibraryItemProgressIndicator(id: itemId), ), ], ), @@ -77,11 +74,7 @@ class LibraryItemHeroSection extends HookConsumerWidget { } class _BookDetails extends HookConsumerWidget { - const _BookDetails({ - super.key, - required this.id, - this.extraMap, - }); + const _BookDetails({required this.id, this.extraMap}); final String id; final LibraryItemExtras? extraMap; @@ -91,7 +84,7 @@ class _BookDetails extends HookConsumerWidget { final itemFromApi = ref.watch(libraryItemProvider(id)); final itemBookMetadata = - itemFromApi.valueOrNull?.media.metadata.asBookMetadataExpanded; + itemFromApi.value?.media.metadata.asBookMetadataExpanded; return Expanded( child: Padding( @@ -100,10 +93,7 @@ class _BookDetails extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - _BookTitle( - extraMap: extraMap, - itemBookMetadata: itemBookMetadata, - ), + _BookTitle(extraMap: extraMap, itemBookMetadata: itemBookMetadata), Container( margin: const EdgeInsets.symmetric(vertical: 16), child: Column( @@ -135,17 +125,14 @@ class _BookDetails extends HookConsumerWidget { } class _LibraryItemProgressIndicator extends HookConsumerWidget { - const _LibraryItemProgressIndicator({ - super.key, - required this.id, - }); + const _LibraryItemProgressIndicator({required this.id}); final String id; @override Widget build(BuildContext context, WidgetRef ref) { final player = ref.watch(audiobookPlayerProvider); - final libraryItem = ref.watch(libraryItemProvider(id)).valueOrNull; + final libraryItem = ref.watch(libraryItemProvider(id)).value; if (libraryItem == null) { return const SizedBox.shrink(); } @@ -159,13 +146,15 @@ class _LibraryItemProgressIndicator extends HookConsumerWidget { Duration remainingTime; if (player.book?.libraryItemId == libraryItem.id) { // final positionStream = useStream(player.slowPositionStream); - progress = (player.positionInBook).inSeconds / + progress = + (player.positionInBook).inSeconds / libraryItem.media.asBookExpanded.duration.inSeconds; remainingTime = libraryItem.media.asBookExpanded.duration - player.positionInBook; } else { progress = mediaProgress?.progress ?? 0; - remainingTime = (libraryItem.media.asBookExpanded.duration - + remainingTime = + (libraryItem.media.asBookExpanded.duration - mediaProgress!.currentTime); } @@ -192,18 +181,17 @@ class _LibraryItemProgressIndicator extends HookConsumerWidget { semanticsLabel: 'Book progress', semanticsValue: '${progressInPercent.toStringAsFixed(2)}%', ), - const SizedBox.square( - dimension: 4.0, - ), + const SizedBox.square(dimension: 4.0), // time remaining Text( // only show 2 decimal places '${remainingTime.smartBinaryFormat} left', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: - Theme.of(context).colorScheme.onSurface.withOpacity(0.75), - ), + color: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.75), + ), ), ], ), @@ -212,11 +200,7 @@ class _LibraryItemProgressIndicator extends HookConsumerWidget { } class _HeroSectionSubLabelWithIcon extends HookConsumerWidget { - const _HeroSectionSubLabelWithIcon({ - super.key, - required this.icon, - required this.text, - }); + const _HeroSectionSubLabelWithIcon({required this.icon, required this.text}); final IconData icon; final Widget text; @@ -226,11 +210,13 @@ class _HeroSectionSubLabelWithIcon extends HookConsumerWidget { final themeData = Theme.of(context); final useFontAwesome = icon.runtimeType == FontAwesomeIcons.book.runtimeType; - final useMaterialThemeOnItemPage = - ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage; + final useMaterialThemeOnItemPage = ref + .watch(appSettingsProvider) + .themeSettings + .useMaterialThemeOnItemPage; final color = useMaterialThemeOnItemPage ? themeData.colorScheme.primary - : themeData.colorScheme.onSurface.withOpacity(0.75); + : themeData.colorScheme.onSurface.withValues(alpha: 0.75); return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Row( @@ -238,20 +224,10 @@ class _HeroSectionSubLabelWithIcon extends HookConsumerWidget { Container( margin: const EdgeInsets.only(right: 8, top: 2), child: useFontAwesome - ? FaIcon( - icon, - size: 16, - color: color, - ) - : Icon( - icon, - size: 16, - color: color, - ), - ), - Expanded( - child: text, + ? FaIcon(icon, size: 16, color: color) + : Icon(icon, size: 16, color: color), ), + Expanded(child: text), ], ), ); @@ -260,7 +236,6 @@ class _HeroSectionSubLabelWithIcon extends HookConsumerWidget { class _BookSeries extends StatelessWidget { const _BookSeries({ - super.key, required this.itemBookMetadata, required this.bookDetailsCached, }); @@ -306,7 +281,6 @@ class _BookSeries extends StatelessWidget { class _BookNarrators extends StatelessWidget { const _BookNarrators({ - super.key, required this.itemBookMetadata, required this.bookDetailsCached, }); @@ -341,10 +315,7 @@ class _BookNarrators extends StatelessWidget { } class _BookCover extends HookConsumerWidget { - const _BookCover({ - super.key, - required this.itemId, - }); + const _BookCover({required this.itemId}); final String itemId; @@ -353,25 +324,27 @@ class _BookCover extends HookConsumerWidget { final coverImage = ref.watch(coverImageProvider(itemId)); final themeData = Theme.of(context); // final item = ref.watch(libraryItemProvider(itemId)); - final useMaterialThemeOnItemPage = - ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage; + final themeSettings = ref.watch(appSettingsProvider).themeSettings; ColorScheme? coverColorScheme; - if (useMaterialThemeOnItemPage) { + if (themeSettings.useMaterialThemeOnItemPage) { coverColorScheme = ref .watch( themeOfLibraryItemProvider( itemId, brightness: Theme.of(context).brightness, + highContrast: + themeSettings.highContrast || + MediaQuery.of(context).highContrast, ), ) - .valueOrNull; + .value; } return ThemeSwitcher( builder: (context) { // change theme after 2 seconds - if (useMaterialThemeOnItemPage) { + if (themeSettings.useMaterialThemeOnItemPage) { Future.delayed(150.ms, () { try { ThemeSwitcher.of(context).changeTheme( @@ -383,7 +356,7 @@ class _BookCover extends HookConsumerWidget { : themeData, ); } catch (e) { - appLogger.shout('Error changing theme: $e'); + appLogger.severe('Error changing theme: $e'); } }); } @@ -394,15 +367,10 @@ class _BookCover extends HookConsumerWidget { return const Icon(Icons.error); } - return Image.memory( - image, - fit: BoxFit.cover, - ); + return Image.memory(image, fit: BoxFit.cover); }, loading: () { - return const Center( - child: BookCoverSkeleton(), - ); + return const Center(child: BookCoverSkeleton()); }, error: (error, stack) { return const Center(child: Icon(Icons.error)); @@ -414,11 +382,7 @@ class _BookCover extends HookConsumerWidget { } class _BookTitle extends StatelessWidget { - const _BookTitle({ - super.key, - required this.extraMap, - required this.itemBookMetadata, - }); + const _BookTitle({required this.extraMap, required this.itemBookMetadata}); final LibraryItemExtras? extraMap; final shelfsdk.BookMetadataExpanded? itemBookMetadata; @@ -430,7 +394,8 @@ class _BookTitle extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Hero( - tag: HeroTagPrefixes.bookTitle + + tag: + HeroTagPrefixes.bookTitle + // itemId + (extraMap?.heroTagSuffix ?? ''), child: Text( @@ -449,7 +414,7 @@ class _BookTitle extends StatelessWidget { ? const SizedBox.shrink() : Text( style: themeData.textTheme.titleSmall?.copyWith( - color: themeData.colorScheme.onSurface.withOpacity(0.8), + color: themeData.colorScheme.onSurface.withValues(alpha: 0.8), ), itemBookMetadata?.subtitle ?? '', ), @@ -460,7 +425,6 @@ class _BookTitle extends StatelessWidget { class _BookAuthors extends StatelessWidget { const _BookAuthors({ - super.key, required this.itemBookMetadata, required this.bookDetailsCached, }); diff --git a/lib/features/item_viewer/view/library_item_metadata.dart b/lib/features/item_viewer/view/library_item_metadata.dart index 5874a92..e12a997 100644 --- a/lib/features/item_viewer/view/library_item_metadata.dart +++ b/lib/features/item_viewer/view/library_item_metadata.dart @@ -4,16 +4,13 @@ import 'package:vaani/api/library_item_provider.dart'; import 'package:vaani/shared/extensions/model_conversions.dart'; class LibraryItemMetadata extends HookConsumerWidget { - const LibraryItemMetadata({ - super.key, - required this.id, - }); + const LibraryItemMetadata({super.key, required this.id}); final String id; @override Widget build(BuildContext context, WidgetRef ref) { - final item = ref.watch(libraryItemProvider(id)).valueOrNull; + final item = ref.watch(libraryItemProvider(id)).value; /// formats the duration of the book as `10h 30m` /// @@ -72,7 +69,8 @@ class LibraryItemMetadata extends HookConsumerWidget { ), _MetadataItem( title: 'Published', - value: itemBookMetadata?.publishedDate ?? + value: + itemBookMetadata?.publishedDate ?? itemBookMetadata?.publishedYear ?? 'Unknown', ), @@ -87,19 +85,18 @@ class LibraryItemMetadata extends HookConsumerWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, // alternate between metadata and vertical divider - children: List.generate( - children.length * 2 - 1, - (index) { - if (index.isEven) { - return children[index ~/ 2]; - } - return VerticalDivider( - indent: 6, - endIndent: 6, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), - ); - }, - ), + children: List.generate(children.length * 2 - 1, (index) { + if (index.isEven) { + return children[index ~/ 2]; + } + return VerticalDivider( + indent: 6, + endIndent: 6, + color: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.6), + ); + }), ), ), ); @@ -108,11 +105,7 @@ class LibraryItemMetadata extends HookConsumerWidget { /// key-value pair to display as column class _MetadataItem extends StatelessWidget { - const _MetadataItem({ - super.key, - required this.title, - required this.value, - }); + const _MetadataItem({required this.title, required this.value}); final String title; final String value; @@ -126,7 +119,7 @@ class _MetadataItem extends StatelessWidget { children: [ Text( style: themeData.textTheme.titleMedium?.copyWith( - color: themeData.colorScheme.onSurface.withOpacity(0.90), + color: themeData.colorScheme.onSurface.withValues(alpha: 0.90), ), value, maxLines: 1, @@ -134,7 +127,7 @@ class _MetadataItem extends StatelessWidget { ), Text( style: themeData.textTheme.bodySmall?.copyWith( - color: themeData.colorScheme.onSurface.withOpacity(0.7), + color: themeData.colorScheme.onSurface.withValues(alpha: 0.7), ), title, maxLines: 1, diff --git a/lib/features/item_viewer/view/library_item_page.dart b/lib/features/item_viewer/view/library_item_page.dart index 9e98003..6974bc6 100644 --- a/lib/features/item_viewer/view/library_item_page.dart +++ b/lib/features/item_viewer/view/library_item_page.dart @@ -3,10 +3,11 @@ import 'dart:math'; import 'package:animated_theme_switcher/animated_theme_switcher.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:vaani/api/library_item_provider.dart'; import 'package:vaani/features/item_viewer/view/library_item_sliver_app_bar.dart'; -import 'package:vaani/features/player/providers/player_form.dart'; +import 'package:vaani/features/player/view/mini_player_bottom_padding.dart'; import 'package:vaani/router/models/library_item_extras.dart'; import 'package:vaani/shared/widgets/expandable_description.dart'; @@ -15,27 +16,86 @@ import 'library_item_hero_section.dart'; import 'library_item_metadata.dart'; class LibraryItemPage extends HookConsumerWidget { - const LibraryItemPage({ - super.key, - required this.itemId, - this.extra, - }); + const LibraryItemPage({super.key, required this.itemId, this.extra}); final String itemId; final Object? extra; + static const double _showFabThreshold = 300.0; @override Widget build(BuildContext context, WidgetRef ref) { - final additionalItemData = - extra is LibraryItemExtras ? extra as LibraryItemExtras : null; + final additionalItemData = extra is LibraryItemExtras + ? extra as LibraryItemExtras + : null; + final scrollController = useScrollController(); + final showFab = useState(false); + + // Effect to listen to scroll changes and update FAB visibility + useEffect(() { + void listener() { + if (!scrollController.hasClients) { + return; // Ensure controller is attached + } + final shouldShow = scrollController.offset > _showFabThreshold; + // Update state only if it changes and widget is still mounted + if (showFab.value != shouldShow && context.mounted) { + showFab.value = shouldShow; + } + } + + scrollController.addListener(listener); + // Initial check in case the view starts scrolled (less likely but safe) + WidgetsBinding.instance.addPostFrameCallback((_) { + if (scrollController.hasClients && context.mounted) { + listener(); + } + }); + + // Cleanup: remove the listener when the widget is disposed + return () => scrollController.removeListener(listener); + }, [scrollController]); // Re-run effect if scrollController changes + + // --- FAB Scroll-to-Top Logic --- + void scrollToTop() { + if (scrollController.hasClients) { + scrollController.animateTo( + 0.0, // Target offset (top) + duration: 300.ms, + curve: Curves.easeInOut, + ); + } + } return ThemeProvider( initTheme: Theme.of(context), duration: 200.ms, child: ThemeSwitchingArea( child: Scaffold( + floatingActionButton: AnimatedSwitcher( + duration: 250.ms, + // A common transition for FABs (fade + scale) + transitionBuilder: (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, + child: FadeTransition(opacity: animation, child: child), + ); + }, + child: showFab.value + ? FloatingActionButton( + // Key is important for AnimatedSwitcher to differentiate + key: const ValueKey('fab-scroll-top'), + onPressed: scrollToTop, + tooltip: 'Scroll to top', + child: const Icon(Icons.arrow_upward), + ) + : const SizedBox.shrink(key: ValueKey('fab-empty')), + ), body: CustomScrollView( + controller: scrollController, slivers: [ - const LibraryItemSliverAppBar(), + LibraryItemSliverAppBar( + id: itemId, + scrollController: scrollController, + ), SliverPadding( padding: const EdgeInsets.all(8), sliver: LibraryItemHeroSection( @@ -44,21 +104,13 @@ class LibraryItemPage extends HookConsumerWidget { ), ), // a horizontal display with dividers of metadata - SliverToBoxAdapter( - child: LibraryItemMetadata(id: itemId), - ), + SliverToBoxAdapter(child: LibraryItemMetadata(id: itemId)), // a row of actions like play, download, share, etc - SliverToBoxAdapter( - child: LibraryItemActions(id: itemId), - ), + SliverToBoxAdapter(child: LibraryItemActions(id: itemId)), // a expandable section for book description - SliverToBoxAdapter( - child: LibraryItemDescription(id: itemId), - ), + SliverToBoxAdapter(child: LibraryItemDescription(id: itemId)), // a padding at the bottom to make sure the last item is not hidden by mini player - const SliverToBoxAdapter( - child: SizedBox(height: playerMinHeight), - ), + const SliverToBoxAdapter(child: MiniPlayerBottomPadding()), ], ), ), @@ -68,15 +120,12 @@ class LibraryItemPage extends HookConsumerWidget { } class LibraryItemDescription extends HookConsumerWidget { - const LibraryItemDescription({ - super.key, - required this.id, - }); + const LibraryItemDescription({super.key, required this.id}); final String id; @override Widget build(BuildContext context, WidgetRef ref) { - final item = ref.watch(libraryItemProvider(id)).valueOrNull; + final item = ref.watch(libraryItemProvider(id)).value; if (item == null) { return const SizedBox(); } @@ -91,16 +140,21 @@ class LibraryItemDescription extends HookConsumerWidget { double calculateWidth( BuildContext context, BoxConstraints constraints, { + /// width ratio of the cover image to the available width double widthRatio = 0.4, /// height ratio of the cover image to the available height double maxHeightToUse = 0.25, }) { - final availHeight = - min(constraints.maxHeight, MediaQuery.of(context).size.height); - final availWidth = - min(constraints.maxWidth, MediaQuery.of(context).size.width); + final availHeight = min( + constraints.maxHeight, + MediaQuery.of(context).size.height, + ); + final availWidth = min( + constraints.maxWidth, + MediaQuery.of(context).size.width, + ); // make the width widthRatio of the available width var width = availWidth * widthRatio; diff --git a/lib/features/item_viewer/view/library_item_sliver_app_bar.dart b/lib/features/item_viewer/view/library_item_sliver_app_bar.dart index 99068e2..5090160 100644 --- a/lib/features/item_viewer/view/library_item_sliver_app_bar.dart +++ b/lib/features/item_viewer/view/library_item_sliver_app_bar.dart @@ -1,23 +1,78 @@ import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:vaani/api/library_item_provider.dart' show libraryItemProvider; -class LibraryItemSliverAppBar extends StatelessWidget { +class LibraryItemSliverAppBar extends HookConsumerWidget { const LibraryItemSliverAppBar({ super.key, + required this.id, + required this.scrollController, }); + final String id; + final ScrollController scrollController; + + static const double _showTitleThreshold = kToolbarHeight * 0.5; + @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final item = ref.watch(libraryItemProvider(id)).value; + + final showTitle = useState(false); + + useEffect(() { + void listener() { + final shouldShow = + scrollController.hasClients && + scrollController.offset > _showTitleThreshold; + if (showTitle.value != shouldShow) { + showTitle.value = shouldShow; + } + } + + scrollController.addListener(listener); + // Trigger listener once initially in case the view starts scrolled + // (though unlikely for this specific use case, it's good practice) + WidgetsBinding.instance.addPostFrameCallback((_) { + if (scrollController.hasClients) { + listener(); + } + }); + return () => scrollController.removeListener(listener); + }, [scrollController]); + return SliverAppBar( - backgroundColor: Colors.transparent, elevation: 0, - floating: true, + floating: false, + pinned: true, primary: true, - snap: true, actions: [ - // cast button - IconButton(onPressed: () {}, icon: const Icon(Icons.cast)), - IconButton(onPressed: () {}, icon: const Icon(Icons.more_vert)), + // IconButton( + // icon: const Icon(Icons.cast), + // onPressed: () { + // // Handle search action + // }, + // ), ], + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 150), + child: showTitle.value + ? Text( + // Use a Key to help AnimatedSwitcher differentiate widgets + key: const ValueKey('title-text'), + item?.media.metadata.title ?? '', + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium, + ) + : const SizedBox( + // Also give it a key for differentiation + key: ValueKey('empty-title'), + width: 0, // Ensure it takes no space if possible + height: 0, + ), + ), + centerTitle: false, ); } } diff --git a/lib/features/library_browser/view/library_browser_page.dart b/lib/features/library_browser/view/library_browser_page.dart index d12de57..aa03fa8 100644 --- a/lib/features/library_browser/view/library_browser_page.dart +++ b/lib/features/library_browser/view/library_browser_page.dart @@ -1,47 +1,81 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:vaani/router/router.dart'; +import 'package:vaani/api/library_provider.dart' show currentLibraryProvider; +import 'package:vaani/features/you/view/widgets/library_switch_chip.dart' + show showLibrarySwitcher; +import 'package:vaani/router/router.dart' show Routes; +import 'package:vaani/shared/icons/abs_icons.dart' show AbsIcons; +import 'package:vaani/shared/widgets/not_implemented.dart' + show showNotImplementedToast; class LibraryBrowserPage extends HookConsumerWidget { const LibraryBrowserPage({super.key}); - @override Widget build(BuildContext context, WidgetRef ref) { + final currentLibrary = ref.watch(currentLibraryProvider).value; + + // Determine the icon to use, with a fallback + final IconData libraryIconData = + AbsIcons.getIconByName(currentLibrary?.icon) ?? Icons.library_books; + + // Determine the title text + final String appBarTitle = '${currentLibrary?.name ?? 'Your'} Library'; + return Scaffold( - appBar: AppBar( - title: const Text('Library'), - backgroundColor: Colors.transparent, - ), - // a list redirecting to authors, genres, and series pages - body: ListView( - children: [ - ListTile( - title: const Text('Authors'), - leading: const Icon(Icons.person), - trailing: const Icon(Icons.chevron_right), - onTap: () {}, + // Use CustomScrollView to enable slivers + body: CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + // floating: true, // Optional: uncomment if you want floating behavior + // snap: + // true, // Optional: uncomment if you want snapping behavior (usually with floating: true) + leading: IconButton( + icon: Icon(libraryIconData), + tooltip: 'Switch Library', // Helpful tooltip for users + onPressed: () { + showLibrarySwitcher(context, ref); + }, + ), + title: Text(appBarTitle), ), - ListTile( - title: const Text('Genres'), - leading: const Icon(Icons.category), - trailing: const Icon(Icons.chevron_right), - onTap: () {}, - ), - ListTile( - title: const Text('Series'), - leading: const Icon(Icons.list), - trailing: const Icon(Icons.chevron_right), - onTap: () {}, - ), - // Downloads - ListTile( - title: const Text('Downloads'), - leading: const Icon(Icons.download), - trailing: const Icon(Icons.chevron_right), - onTap: () { - GoRouter.of(context).pushNamed(Routes.downloads.name); - }, + SliverList( + delegate: SliverChildListDelegate([ + ListTile( + title: const Text('Authors'), + leading: const Icon(Icons.person), + trailing: const Icon(Icons.chevron_right), + onTap: () { + showNotImplementedToast(context); + }, + ), + ListTile( + title: const Text('Genres'), + leading: const Icon(Icons.category), + trailing: const Icon(Icons.chevron_right), + onTap: () { + showNotImplementedToast(context); + }, + ), + ListTile( + title: const Text('Series'), + leading: const Icon(Icons.list), + trailing: const Icon(Icons.chevron_right), + onTap: () { + showNotImplementedToast(context); + }, + ), + // Downloads + ListTile( + title: const Text('Downloads'), + leading: const Icon(Icons.download), + trailing: const Icon(Icons.chevron_right), + onTap: () { + GoRouter.of(context).pushNamed(Routes.downloads.name); + }, + ), + ]), ), ], ), diff --git a/lib/features/logging/core/logger.dart b/lib/features/logging/core/logger.dart new file mode 100644 index 0000000..52eb099 --- /dev/null +++ b/lib/features/logging/core/logger.dart @@ -0,0 +1,36 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; +import 'package:logging_appenders/logging_appenders.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:vaani/shared/extensions/duration_format.dart'; + +Future getLoggingFilePath() async { + final Directory directory = await getApplicationDocumentsDirectory(); + return '${directory.path}/vaani.log'; +} + +Future initLogging() async { + final formatter = const DefaultLogRecordFormatter(); + if (kReleaseMode) { + Logger.root.level = Level.INFO; // is also the default + // Write to a file + RotatingFileAppender( + baseFilePath: await getLoggingFilePath(), + formatter: formatter, + ).attachToLogger(Logger.root); + } else { + Logger.root.level = Level.FINE; // Capture all logs + RotatingFileAppender( + baseFilePath: await getLoggingFilePath(), + formatter: formatter, + ).attachToLogger(Logger.root); + Logger.root.onRecord.listen((record) { + // Print log records to the console + debugPrint( + '${record.loggerName}: ${record.level.name}: ${record.time.time}: ${record.message}', + ); + }); + } +} diff --git a/lib/features/logging/providers/logs_provider.dart b/lib/features/logging/providers/logs_provider.dart new file mode 100644 index 0000000..763fa90 --- /dev/null +++ b/lib/features/logging/providers/logs_provider.dart @@ -0,0 +1,90 @@ +import 'dart:io'; + +import 'package:archive/archive_io.dart'; +import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:vaani/features/logging/core/logger.dart'; +part 'logs_provider.g.dart'; + +@riverpod +class Logs extends _$Logs { + @override + Future> build() async { + final path = await getLoggingFilePath(); + final file = File(path); + if (!file.existsSync()) { + return []; + } + final lines = await file.readAsLines(); + return lines.map(parseLogLine).toList(); + } + + Future clear() async { + final path = await getLoggingFilePath(); + final file = File(path); + await file.writeAsString(''); + state = AsyncData([]); + } + + Future getZipFilePath() async { + final String targetZipPath = await generateZipFilePath(); + var encoder = ZipFileEncoder(); + encoder.create(targetZipPath); + final logFilePath = await getLoggingFilePath(); + final logFile = File(logFilePath); + if (await logFile.exists()) { + // Check if log file exists before adding + await encoder.addFile(logFile); + } else { + // Handle case where log file doesn't exist? Maybe log a warning? + // Or create an empty file inside the zip? For now, just don't add. + debugPrint( + 'Warning: Log file not found at $logFilePath, creating potentially empty zip.', + ); + } + await encoder.close(); + return targetZipPath; + } +} + +Future generateZipFilePath() async { + Directory appDocDirectory = await getTemporaryDirectory(); + return '${appDocDirectory.path}/${generateZipFileName()}'; +} + +String generateZipFileName() { + return 'vaani-${DateTime.now().microsecondsSinceEpoch}.zip'; +} + +Level parseLevel(String level) { + return Level.LEVELS.firstWhere( + (l) => l.name == level, + orElse: () => Level.ALL, + ); +} + +LogRecord parseLogLine(String line) { + // 2024-10-03 00:48:58.012400 INFO GoRouter - getting location for name: "logs" + + final RegExp logLineRegExp = RegExp( + r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}) (\w+) (\w+) - (.+)', + ); + + final match = logLineRegExp.firstMatch(line); + if (match == null) { + // return as is + return LogRecord(Level.ALL, line, 'Unknown'); + } + + final timeString = match.group(1)!; + final levelString = match.group(2)!; + final loggerName = match.group(3)!; + final message = match.group(4)!; + + final time = DateTime.parse(timeString); + final level = parseLevel(levelString); + + return LogRecord(level, message, loggerName, time); +} diff --git a/lib/features/logging/providers/logs_provider.g.dart b/lib/features/logging/providers/logs_provider.g.dart new file mode 100644 index 0000000..c7bb6f8 --- /dev/null +++ b/lib/features/logging/providers/logs_provider.g.dart @@ -0,0 +1,53 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'logs_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(Logs) +final logsProvider = LogsProvider._(); + +final class LogsProvider extends $AsyncNotifierProvider> { + LogsProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'logsProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$logsHash(); + + @$internal + @override + Logs create() => Logs(); +} + +String _$logsHash() => r'aa9d3d56586cba6ddf69615320ea605d071ea5e2'; + +abstract class _$Logs extends $AsyncNotifier> { + FutureOr> build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref>, List>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier>, List>, + AsyncValue>, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/features/logging/view/logs_page.dart b/lib/features/logging/view/logs_page.dart new file mode 100644 index 0000000..978fd19 --- /dev/null +++ b/lib/features/logging/view/logs_page.dart @@ -0,0 +1,306 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:logging/logging.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:vaani/features/logging/providers/logs_provider.dart'; +import 'package:vaani/main.dart'; + +class LogsPage extends HookConsumerWidget { + const LogsPage({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final logs = ref.watch(logsProvider); + final theme = Theme.of(context); + final scrollController = useScrollController(); + return Scaffold( + appBar: AppBar( + title: const Text('Logs'), + actions: [ + IconButton( + tooltip: 'Clear logs', + icon: const Icon(Icons.delete_forever), + onPressed: () async { + // ask for confirmation + final shouldClear = await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Clear logs?'), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + child: const Text('Clear'), + ), + ], + ); + }, + ); + if (shouldClear == true) { + ref.read(logsProvider.notifier).clear(); + } + }, + ), + IconButton( + tooltip: 'Share logs', + icon: const Icon(Icons.share), + onPressed: () async { + appLogger.info('Preparing logs for sharing'); + final zipLogFilePath = await ref + .read(logsProvider.notifier) + .getZipFilePath(); + + // submit logs + final result = await Share.shareXFiles([XFile(zipLogFilePath)]); + + switch (result.status) { + case ShareResultStatus.success: + appLogger.info('Share success'); + break; + case ShareResultStatus.dismissed: + appLogger.info('Share dismissed'); + break; + case ShareResultStatus.unavailable: + appLogger.severe('Share unavailable'); + break; + } + }, + ), + + // downloads disabled since manage external storage permission was removed + // see https://gitlab.com/IzzyOnDroid/repo/-/issues/623#note_2240386369 + // IconButton( + // tooltip: 'Download logs', + // icon: const Icon(Icons.download), + // onPressed: () async { + // appLogger.info('Preparing logs for download'); + + // if (Platform.isAndroid) { + // final androidVersion = + // await ref.watch(deviceSdkVersionProvider.future); + + // if ((int.parse(androidVersion)) > 29) { + // final status = await Permission.storage.status; + // if (!status.isGranted) { + // appLogger + // .info('Requesting storage permission'); + // final newStatus = + // await Permission.storage.request(); + // if (!newStatus.isGranted) { + // appLogger + // .warning('storage permission denied'); + // ScaffoldMessenger.of(context).showSnackBar( + // const SnackBar( + // content: Text('Storage permission denied'), + // ), + // ); + // return; + // } + // } + // } else { + // final status = await Permission.storage.status; + // if (!status.isGranted) { + // appLogger.info('Requesting storage permission'); + // final newStatus = await Permission.storage.request(); + // if (!newStatus.isGranted) { + // appLogger.warning('Storage permission denied'); + // ScaffoldMessenger.of(context).showSnackBar( + // const SnackBar( + // content: Text('Storage permission denied'), + // ), + // ); + // return; + // } + // } + // } + // } + // final zipLogFilePath = + // await ref.read(logsProvider.notifier).getZipFilePath(); + + // // save to folder + // String? outputFile = await FilePicker.platform.saveFile( + // dialogTitle: 'Please select an output file:', + // fileName: zipLogFilePath.split('/').last, + // bytes: await File(zipLogFilePath).readAsBytes(), + // ); + // if (outputFile != null) { + // try { + // final file = File(outputFile); + // final zipFile = File(zipLogFilePath); + // await zipFile.copy(file.path); + // appLogger.info('File saved to: $outputFile'); + // } catch (e) { + // appLogger.severe('Error saving file: $e'); + // } + // } else { + // appLogger.info('Download cancelled'); + // } + // }, + // ), + IconButton( + tooltip: 'Refresh logs', + icon: const Icon(Icons.refresh), + onPressed: () { + ref.invalidate(logsProvider); + }, + ), + IconButton( + tooltip: 'Scroll to top', + icon: const Icon(Icons.arrow_upward), + onPressed: () { + scrollController.animateTo( + 0, + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + ); + }, + ), + ], + ), + // a column with ListView.builder and a scrollable list of logs + body: Column( + children: [ + // a filter for log levels, loggers, and search + // TODO: implement filters and search + Expanded( + child: logs.when( + data: (logRecords) { + return Scrollbar( + controller: scrollController, + child: ListView.builder( + controller: scrollController, + itemCount: logRecords.length, + itemBuilder: (context, index) { + final logRecord = logRecords[index]; + return LogRecordTile(logRecord: logRecord, theme: theme); + }, + ), + ); + }, + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stackTrace) => Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Error loading logs'), + ElevatedButton( + onPressed: () { + ref.invalidate(logsProvider); + }, + child: const Text('Retry'), + ), + ], + ), + ), + ), + ), + ], + ), + ); + } +} + +class LogRecordTile extends StatelessWidget { + final LogRecord logRecord; + final ThemeData theme; + const LogRecordTile({ + required this.logRecord, + required this.theme, + super.key, + }); + @override + Widget build(BuildContext context) { + return ListTile( + leading: CircleAvatar( + backgroundColor: getLogLevelColor(logRecord.level, theme), + child: Icon( + getLogLevelIcon(logRecord.level), + color: getLogLevelTextColor(logRecord.level, theme), + ), + ), + title: Text(logRecord.loggerName), + subtitle: Text(logRecord.message), + onTap: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + icon: Icon(getLogLevelIcon(logRecord.level)), + title: Text(logRecord.loggerName), + content: Text.rich( + TextSpan( + children: [ + TextSpan( + text: logRecord.time.toIso8601String(), + style: const TextStyle(fontStyle: FontStyle.italic), + ), + const TextSpan(text: '\n\n'), + TextSpan(text: logRecord.message), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Close'), + ), + ], + ); + }, + ); + }, + ); + } +} + +IconData getLogLevelIcon(Level level) { + switch (level) { + case Level.INFO: + return (Icons.info); + case Level.WARNING: + return (Icons.warning); + case Level.SEVERE: + case Level.SHOUT: + return (Icons.error); + default: + return (Icons.bug_report); + } +} + +Color? getLogLevelColor(Level level, ThemeData theme) { + switch (level) { + case Level.INFO: + return theme.colorScheme.surfaceContainerLow; + case Level.WARNING: + return theme.colorScheme.surfaceBright; + case Level.SEVERE: + case Level.SHOUT: + return theme.colorScheme.errorContainer; + default: + return theme.colorScheme.primaryContainer; + } +} + +Color? getLogLevelTextColor(Level level, ThemeData theme) { + switch (level) { + case Level.INFO: + return theme.colorScheme.onSurface; + case Level.WARNING: + return theme.colorScheme.onSurface; + case Level.SEVERE: + case Level.SHOUT: + return theme.colorScheme.onErrorContainer; + default: + return theme.colorScheme.onPrimaryContainer; + } +} diff --git a/lib/features/onboarding/models/flow.dart b/lib/features/onboarding/models/flow.dart index 80e8514..46539f6 100644 --- a/lib/features/onboarding/models/flow.dart +++ b/lib/features/onboarding/models/flow.dart @@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'flow.freezed.dart'; @freezed -class Flow with _$Flow { +sealed class Flow with _$Flow { const factory Flow({ required Uri serverUri, required String state, diff --git a/lib/features/onboarding/models/flow.freezed.dart b/lib/features/onboarding/models/flow.freezed.dart index 1437f2e..f79c8bb 100644 --- a/lib/features/onboarding/models/flow.freezed.dart +++ b/lib/features/onboarding/models/flow.freezed.dart @@ -1,5 +1,5 @@ -// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark @@ -9,241 +9,272 @@ part of 'flow.dart'; // FreezedGenerator // ************************************************************************** +// dart format off T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - /// @nodoc mixin _$Flow { - Uri get serverUri => throw _privateConstructorUsedError; - String get state => throw _privateConstructorUsedError; - String get verifier => throw _privateConstructorUsedError; - Cookie get cookie => throw _privateConstructorUsedError; - bool get isFlowComplete => throw _privateConstructorUsedError; - String? get authToken => throw _privateConstructorUsedError; - /// Create a copy of Flow - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $FlowCopyWith get copyWith => throw _privateConstructorUsedError; + Uri get serverUri; String get state; String get verifier; Cookie get cookie; bool get isFlowComplete; String? get authToken; +/// Create a copy of Flow +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$FlowCopyWith get copyWith => _$FlowCopyWithImpl(this as Flow, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is Flow&&(identical(other.serverUri, serverUri) || other.serverUri == serverUri)&&(identical(other.state, state) || other.state == state)&&(identical(other.verifier, verifier) || other.verifier == verifier)&&(identical(other.cookie, cookie) || other.cookie == cookie)&&(identical(other.isFlowComplete, isFlowComplete) || other.isFlowComplete == isFlowComplete)&&(identical(other.authToken, authToken) || other.authToken == authToken)); +} + + +@override +int get hashCode => Object.hash(runtimeType,serverUri,state,verifier,cookie,isFlowComplete,authToken); + +@override +String toString() { + return 'Flow(serverUri: $serverUri, state: $state, verifier: $verifier, cookie: $cookie, isFlowComplete: $isFlowComplete, authToken: $authToken)'; +} + + } /// @nodoc -abstract class $FlowCopyWith<$Res> { - factory $FlowCopyWith(Flow value, $Res Function(Flow) then) = - _$FlowCopyWithImpl<$Res, Flow>; - @useResult - $Res call( - {Uri serverUri, - String state, - String verifier, - Cookie cookie, - bool isFlowComplete, - String? authToken}); -} +abstract mixin class $FlowCopyWith<$Res> { + factory $FlowCopyWith(Flow value, $Res Function(Flow) _then) = _$FlowCopyWithImpl; +@useResult +$Res call({ + Uri serverUri, String state, String verifier, Cookie cookie, bool isFlowComplete, String? authToken +}); + + + +} /// @nodoc -class _$FlowCopyWithImpl<$Res, $Val extends Flow> +class _$FlowCopyWithImpl<$Res> implements $FlowCopyWith<$Res> { - _$FlowCopyWithImpl(this._value, this._then); + _$FlowCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final Flow _self; + final $Res Function(Flow) _then; - /// Create a copy of Flow - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? serverUri = null, - Object? state = null, - Object? verifier = null, - Object? cookie = null, - Object? isFlowComplete = null, - Object? authToken = freezed, - }) { - return _then(_value.copyWith( - serverUri: null == serverUri - ? _value.serverUri - : serverUri // ignore: cast_nullable_to_non_nullable - as Uri, - state: null == state - ? _value.state - : state // ignore: cast_nullable_to_non_nullable - as String, - verifier: null == verifier - ? _value.verifier - : verifier // ignore: cast_nullable_to_non_nullable - as String, - cookie: null == cookie - ? _value.cookie - : cookie // ignore: cast_nullable_to_non_nullable - as Cookie, - isFlowComplete: null == isFlowComplete - ? _value.isFlowComplete - : isFlowComplete // ignore: cast_nullable_to_non_nullable - as bool, - authToken: freezed == authToken - ? _value.authToken - : authToken // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); - } +/// Create a copy of Flow +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? serverUri = null,Object? state = null,Object? verifier = null,Object? cookie = null,Object? isFlowComplete = null,Object? authToken = freezed,}) { + return _then(_self.copyWith( +serverUri: null == serverUri ? _self.serverUri : serverUri // ignore: cast_nullable_to_non_nullable +as Uri,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable +as String,verifier: null == verifier ? _self.verifier : verifier // ignore: cast_nullable_to_non_nullable +as String,cookie: null == cookie ? _self.cookie : cookie // ignore: cast_nullable_to_non_nullable +as Cookie,isFlowComplete: null == isFlowComplete ? _self.isFlowComplete : isFlowComplete // ignore: cast_nullable_to_non_nullable +as bool,authToken: freezed == authToken ? _self.authToken : authToken // ignore: cast_nullable_to_non_nullable +as String?, + )); } -/// @nodoc -abstract class _$$FlowImplCopyWith<$Res> implements $FlowCopyWith<$Res> { - factory _$$FlowImplCopyWith( - _$FlowImpl value, $Res Function(_$FlowImpl) then) = - __$$FlowImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {Uri serverUri, - String state, - String verifier, - Cookie cookie, - bool isFlowComplete, - String? authToken}); } -/// @nodoc -class __$$FlowImplCopyWithImpl<$Res> - extends _$FlowCopyWithImpl<$Res, _$FlowImpl> - implements _$$FlowImplCopyWith<$Res> { - __$$FlowImplCopyWithImpl(_$FlowImpl _value, $Res Function(_$FlowImpl) _then) - : super(_value, _then); - /// Create a copy of Flow - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? serverUri = null, - Object? state = null, - Object? verifier = null, - Object? cookie = null, - Object? isFlowComplete = null, - Object? authToken = freezed, - }) { - return _then(_$FlowImpl( - serverUri: null == serverUri - ? _value.serverUri - : serverUri // ignore: cast_nullable_to_non_nullable - as Uri, - state: null == state - ? _value.state - : state // ignore: cast_nullable_to_non_nullable - as String, - verifier: null == verifier - ? _value.verifier - : verifier // ignore: cast_nullable_to_non_nullable - as String, - cookie: null == cookie - ? _value.cookie - : cookie // ignore: cast_nullable_to_non_nullable - as Cookie, - isFlowComplete: null == isFlowComplete - ? _value.isFlowComplete - : isFlowComplete // ignore: cast_nullable_to_non_nullable - as bool, - authToken: freezed == authToken - ? _value.authToken - : authToken // ignore: cast_nullable_to_non_nullable - as String?, - )); - } +/// Adds pattern-matching-related methods to [Flow]. +extension FlowPatterns on Flow { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _Flow value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Flow() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _Flow value) $default,){ +final _that = this; +switch (_that) { +case _Flow(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _Flow value)? $default,){ +final _that = this; +switch (_that) { +case _Flow() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( Uri serverUri, String state, String verifier, Cookie cookie, bool isFlowComplete, String? authToken)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Flow() when $default != null: +return $default(_that.serverUri,_that.state,_that.verifier,_that.cookie,_that.isFlowComplete,_that.authToken);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( Uri serverUri, String state, String verifier, Cookie cookie, bool isFlowComplete, String? authToken) $default,) {final _that = this; +switch (_that) { +case _Flow(): +return $default(_that.serverUri,_that.state,_that.verifier,_that.cookie,_that.isFlowComplete,_that.authToken);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( Uri serverUri, String state, String verifier, Cookie cookie, bool isFlowComplete, String? authToken)? $default,) {final _that = this; +switch (_that) { +case _Flow() when $default != null: +return $default(_that.serverUri,_that.state,_that.verifier,_that.cookie,_that.isFlowComplete,_that.authToken);case _: + return null; + +} +} + } /// @nodoc -class _$FlowImpl implements _Flow { - const _$FlowImpl( - {required this.serverUri, - required this.state, - required this.verifier, - required this.cookie, - this.isFlowComplete = false, - this.authToken}); - @override - final Uri serverUri; - @override - final String state; - @override - final String verifier; - @override - final Cookie cookie; - @override - @JsonKey() - final bool isFlowComplete; - @override - final String? authToken; +class _Flow implements Flow { + const _Flow({required this.serverUri, required this.state, required this.verifier, required this.cookie, this.isFlowComplete = false, this.authToken}); + - @override - String toString() { - return 'Flow(serverUri: $serverUri, state: $state, verifier: $verifier, cookie: $cookie, isFlowComplete: $isFlowComplete, authToken: $authToken)'; - } +@override final Uri serverUri; +@override final String state; +@override final String verifier; +@override final Cookie cookie; +@override@JsonKey() final bool isFlowComplete; +@override final String? authToken; - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$FlowImpl && - (identical(other.serverUri, serverUri) || - other.serverUri == serverUri) && - (identical(other.state, state) || other.state == state) && - (identical(other.verifier, verifier) || - other.verifier == verifier) && - (identical(other.cookie, cookie) || other.cookie == cookie) && - (identical(other.isFlowComplete, isFlowComplete) || - other.isFlowComplete == isFlowComplete) && - (identical(other.authToken, authToken) || - other.authToken == authToken)); - } +/// Create a copy of Flow +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$FlowCopyWith<_Flow> get copyWith => __$FlowCopyWithImpl<_Flow>(this, _$identity); - @override - int get hashCode => Object.hash(runtimeType, serverUri, state, verifier, - cookie, isFlowComplete, authToken); - /// Create a copy of Flow - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$FlowImplCopyWith<_$FlowImpl> get copyWith => - __$$FlowImplCopyWithImpl<_$FlowImpl>(this, _$identity); + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Flow&&(identical(other.serverUri, serverUri) || other.serverUri == serverUri)&&(identical(other.state, state) || other.state == state)&&(identical(other.verifier, verifier) || other.verifier == verifier)&&(identical(other.cookie, cookie) || other.cookie == cookie)&&(identical(other.isFlowComplete, isFlowComplete) || other.isFlowComplete == isFlowComplete)&&(identical(other.authToken, authToken) || other.authToken == authToken)); } -abstract class _Flow implements Flow { - const factory _Flow( - {required final Uri serverUri, - required final String state, - required final String verifier, - required final Cookie cookie, - final bool isFlowComplete, - final String? authToken}) = _$FlowImpl; - @override - Uri get serverUri; - @override - String get state; - @override - String get verifier; - @override - Cookie get cookie; - @override - bool get isFlowComplete; - @override - String? get authToken; +@override +int get hashCode => Object.hash(runtimeType,serverUri,state,verifier,cookie,isFlowComplete,authToken); - /// Create a copy of Flow - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$FlowImplCopyWith<_$FlowImpl> get copyWith => - throw _privateConstructorUsedError; +@override +String toString() { + return 'Flow(serverUri: $serverUri, state: $state, verifier: $verifier, cookie: $cookie, isFlowComplete: $isFlowComplete, authToken: $authToken)'; } + + +} + +/// @nodoc +abstract mixin class _$FlowCopyWith<$Res> implements $FlowCopyWith<$Res> { + factory _$FlowCopyWith(_Flow value, $Res Function(_Flow) _then) = __$FlowCopyWithImpl; +@override @useResult +$Res call({ + Uri serverUri, String state, String verifier, Cookie cookie, bool isFlowComplete, String? authToken +}); + + + + +} +/// @nodoc +class __$FlowCopyWithImpl<$Res> + implements _$FlowCopyWith<$Res> { + __$FlowCopyWithImpl(this._self, this._then); + + final _Flow _self; + final $Res Function(_Flow) _then; + +/// Create a copy of Flow +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? serverUri = null,Object? state = null,Object? verifier = null,Object? cookie = null,Object? isFlowComplete = null,Object? authToken = freezed,}) { + return _then(_Flow( +serverUri: null == serverUri ? _self.serverUri : serverUri // ignore: cast_nullable_to_non_nullable +as Uri,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable +as String,verifier: null == verifier ? _self.verifier : verifier // ignore: cast_nullable_to_non_nullable +as String,cookie: null == cookie ? _self.cookie : cookie // ignore: cast_nullable_to_non_nullable +as Cookie,isFlowComplete: null == isFlowComplete ? _self.isFlowComplete : isFlowComplete // ignore: cast_nullable_to_non_nullable +as bool,authToken: freezed == authToken ? _self.authToken : authToken // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + + +} + +// dart format on diff --git a/lib/features/onboarding/providers/oauth_provider.dart b/lib/features/onboarding/providers/oauth_provider.dart index 79445d9..38446fa 100644 --- a/lib/features/onboarding/providers/oauth_provider.dart +++ b/lib/features/onboarding/providers/oauth_provider.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:vaani/api/api_provider.dart'; import 'package:vaani/models/error_response.dart'; @@ -52,8 +53,10 @@ class OauthFlows extends _$OauthFlows { } state = { ...state, - oauthState: state[oauthState]! - .copyWith(isFlowComplete: true, authToken: authToken), + oauthState: state[oauthState]!.copyWith( + isFlowComplete: true, + authToken: authToken, + ), }; } } @@ -61,7 +64,7 @@ class OauthFlows extends _$OauthFlows { /// the code returned by the server in exchange for the verifier @riverpod Future loginInExchangeForCode( - LoginInExchangeForCodeRef ref, { + Ref ref, { required State oauthState, required Code code, ErrorResponseHandler? responseHandler, diff --git a/lib/features/onboarding/providers/oauth_provider.g.dart b/lib/features/onboarding/providers/oauth_provider.g.dart index 9b7c4f5..d9c7575 100644 --- a/lib/features/onboarding/providers/oauth_provider.g.dart +++ b/lib/features/onboarding/providers/oauth_provider.g.dart @@ -6,219 +6,167 @@ part of 'oauth_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$loginInExchangeForCodeHash() => - r'e931254959d9eb8196439c6b0c884c26cbe17c2f'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); +@ProviderFor(OauthFlows) +final oauthFlowsProvider = OauthFlowsProvider._(); - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// the code returned by the server in exchange for the verifier -/// -/// Copied from [loginInExchangeForCode]. -@ProviderFor(loginInExchangeForCode) -const loginInExchangeForCodeProvider = LoginInExchangeForCodeFamily(); - -/// the code returned by the server in exchange for the verifier -/// -/// Copied from [loginInExchangeForCode]. -class LoginInExchangeForCodeFamily extends Family> { - /// the code returned by the server in exchange for the verifier - /// - /// Copied from [loginInExchangeForCode]. - const LoginInExchangeForCodeFamily(); - - /// the code returned by the server in exchange for the verifier - /// - /// Copied from [loginInExchangeForCode]. - LoginInExchangeForCodeProvider call({ - required String oauthState, - required String code, - ErrorResponseHandler? responseHandler, - }) { - return LoginInExchangeForCodeProvider( - oauthState: oauthState, - code: code, - responseHandler: responseHandler, - ); - } - - @override - LoginInExchangeForCodeProvider getProviderOverride( - covariant LoginInExchangeForCodeProvider provider, - ) { - return call( - oauthState: provider.oauthState, - code: provider.code, - responseHandler: provider.responseHandler, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'loginInExchangeForCodeProvider'; -} - -/// the code returned by the server in exchange for the verifier -/// -/// Copied from [loginInExchangeForCode]. -class LoginInExchangeForCodeProvider - extends AutoDisposeFutureProvider { - /// the code returned by the server in exchange for the verifier - /// - /// Copied from [loginInExchangeForCode]. - LoginInExchangeForCodeProvider({ - required String oauthState, - required String code, - ErrorResponseHandler? responseHandler, - }) : this._internal( - (ref) => loginInExchangeForCode( - ref as LoginInExchangeForCodeRef, - oauthState: oauthState, - code: code, - responseHandler: responseHandler, - ), - from: loginInExchangeForCodeProvider, - name: r'loginInExchangeForCodeProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$loginInExchangeForCodeHash, - dependencies: LoginInExchangeForCodeFamily._dependencies, - allTransitiveDependencies: - LoginInExchangeForCodeFamily._allTransitiveDependencies, - oauthState: oauthState, - code: code, - responseHandler: responseHandler, - ); - - LoginInExchangeForCodeProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.oauthState, - required this.code, - required this.responseHandler, - }) : super.internal(); - - final String oauthState; - final String code; - final ErrorResponseHandler? responseHandler; - - @override - Override overrideWith( - FutureOr Function(LoginInExchangeForCodeRef provider) create, - ) { - return ProviderOverride( - origin: this, - override: LoginInExchangeForCodeProvider._internal( - (ref) => create(ref as LoginInExchangeForCodeRef), - from: from, - name: null, +final class OauthFlowsProvider + extends $NotifierProvider> { + OauthFlowsProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'oauthFlowsProvider', + isAutoDispose: false, dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - oauthState: oauthState, - code: code, - responseHandler: responseHandler, - ), - ); - } + $allTransitiveDependencies: null, + ); @override - AutoDisposeFutureProviderElement createElement() { - return _LoginInExchangeForCodeProviderElement(this); + String debugGetCreateSourceHash() => _$oauthFlowsHash(); + + @$internal + @override + OauthFlows create() => OauthFlows(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Map value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider>(value), + ); + } +} + +String _$oauthFlowsHash() => r'4e278baa0bf26f2a10694ca2caadb68dd5b6156f'; + +abstract class _$OauthFlows extends $Notifier> { + Map build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref, Map>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, Map>, + Map, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} + +/// the code returned by the server in exchange for the verifier + +@ProviderFor(loginInExchangeForCode) +final loginInExchangeForCodeProvider = LoginInExchangeForCodeFamily._(); + +/// the code returned by the server in exchange for the verifier + +final class LoginInExchangeForCodeProvider + extends $FunctionalProvider, String?, FutureOr> + with $FutureModifier, $FutureProvider { + /// the code returned by the server in exchange for the verifier + LoginInExchangeForCodeProvider._({ + required LoginInExchangeForCodeFamily super.from, + required ({ + State oauthState, + Code code, + ErrorResponseHandler? responseHandler, + }) + super.argument, + }) : super( + retry: null, + name: r'loginInExchangeForCodeProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$loginInExchangeForCodeHash(); + + @override + String toString() { + return r'loginInExchangeForCodeProvider' + '' + '$argument'; + } + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + final argument = + this.argument + as ({ + State oauthState, + Code code, + ErrorResponseHandler? responseHandler, + }); + return loginInExchangeForCode( + ref, + oauthState: argument.oauthState, + code: argument.code, + responseHandler: argument.responseHandler, + ); } @override bool operator ==(Object other) { return other is LoginInExchangeForCodeProvider && - other.oauthState == oauthState && - other.code == code && - other.responseHandler == responseHandler; + other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, oauthState.hashCode); - hash = _SystemHash.combine(hash, code.hashCode); - hash = _SystemHash.combine(hash, responseHandler.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -mixin LoginInExchangeForCodeRef on AutoDisposeFutureProviderRef { - /// The parameter `oauthState` of this provider. - String get oauthState; +String _$loginInExchangeForCodeHash() => + r'bfc3945529048a0f536052fd5579b76457560fcd'; - /// The parameter `code` of this provider. - String get code; +/// the code returned by the server in exchange for the verifier - /// The parameter `responseHandler` of this provider. - ErrorResponseHandler? get responseHandler; +final class LoginInExchangeForCodeFamily extends $Family + with + $FunctionalFamilyOverride< + FutureOr, + ({State oauthState, Code code, ErrorResponseHandler? responseHandler}) + > { + LoginInExchangeForCodeFamily._() + : super( + retry: null, + name: r'loginInExchangeForCodeProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + /// the code returned by the server in exchange for the verifier + + LoginInExchangeForCodeProvider call({ + required State oauthState, + required Code code, + ErrorResponseHandler? responseHandler, + }) => LoginInExchangeForCodeProvider._( + argument: ( + oauthState: oauthState, + code: code, + responseHandler: responseHandler, + ), + from: this, + ); + + @override + String toString() => r'loginInExchangeForCodeProvider'; } - -class _LoginInExchangeForCodeProviderElement - extends AutoDisposeFutureProviderElement - with LoginInExchangeForCodeRef { - _LoginInExchangeForCodeProviderElement(super.provider); - - @override - String get oauthState => - (origin as LoginInExchangeForCodeProvider).oauthState; - @override - String get code => (origin as LoginInExchangeForCodeProvider).code; - @override - ErrorResponseHandler? get responseHandler => - (origin as LoginInExchangeForCodeProvider).responseHandler; -} - -String _$oauthFlowsHash() => r'4e278baa0bf26f2a10694ca2caadb68dd5b6156f'; - -/// See also [OauthFlows]. -@ProviderFor(OauthFlows) -final oauthFlowsProvider = - NotifierProvider>.internal( - OauthFlows.new, - name: r'oauthFlowsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$oauthFlowsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$OauthFlows = Notifier>; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/features/onboarding/view/callback_page.dart b/lib/features/onboarding/view/callback_page.dart index dd3de86..bb4e139 100644 --- a/lib/features/onboarding/view/callback_page.dart +++ b/lib/features/onboarding/view/callback_page.dart @@ -27,9 +27,7 @@ class CallbackPage extends HookConsumerWidget { // check if the state is in the flows if (!flows.containsKey(state)) { - return const _SomethingWentWrong( - message: 'State not found', - ); + return const _SomethingWentWrong(message: 'State not found'); } // get the token @@ -45,26 +43,21 @@ class CallbackPage extends HookConsumerWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('Contacting server...\nPlease wait\n\nGot:' - '\nState: $state\nCode: $code'), + Text( + 'Contacting server...\nPlease wait\n\nGot:' + '\nState: $state\nCode: $code', + ), loginAuthToken.when( data: (authenticationToken) { if (authenticationToken == null) { - handleServerError( - context, - serverErrorResponse, - ); + handleServerError(context, serverErrorResponse); return const BackToLoginButton(); } return Text('Token: $authenticationToken'); }, loading: () => const CircularProgressIndicator(), error: (error, _) { - handleServerError( - context, - serverErrorResponse, - e: error, - ); + handleServerError(context, serverErrorResponse, e: error); return Column( children: [ Text('Error with OAuth flow: $error'), @@ -81,9 +74,7 @@ class CallbackPage extends HookConsumerWidget { } class BackToLoginButton extends StatelessWidget { - const BackToLoginButton({ - super.key, - }); + const BackToLoginButton({super.key}); @override Widget build(BuildContext context) { @@ -97,10 +88,7 @@ class BackToLoginButton extends StatelessWidget { } class _SomethingWentWrong extends StatelessWidget { - const _SomethingWentWrong({ - super.key, - this.message = 'Error with OAuth flow', - }); + const _SomethingWentWrong({this.message = 'Error with OAuth flow'}); final String message; @@ -110,10 +98,7 @@ class _SomethingWentWrong extends StatelessWidget { body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(message), - const BackToLoginButton(), - ], + children: [Text(message), const BackToLoginButton()], ), ), ); diff --git a/lib/features/onboarding/view/onboarding_single_page.dart b/lib/features/onboarding/view/onboarding_single_page.dart index 5ceff08..c0e701a 100644 --- a/lib/features/onboarding/view/onboarding_single_page.dart +++ b/lib/features/onboarding/view/onboarding_single_page.dart @@ -9,101 +9,120 @@ import 'package:vaani/shared/utils.dart'; import 'package:vaani/shared/widgets/add_new_server.dart'; class OnboardingSinglePage extends HookConsumerWidget { - const OnboardingSinglePage({ - super.key, - }); + const OnboardingSinglePage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final apiSettings = ref.watch(apiSettingsProvider); - final serverUriController = useTextEditingController( - text: apiSettings.activeServer?.serverUrl.toString() ?? '', - ); - var audiobookshelfUri = makeBaseUrl(serverUriController.text); - - final canUserLogin = useState(apiSettings.activeServer != null); - - fadeSlideTransitionBuilder( - Widget child, - Animation animation, - ) { - return FadeTransition( - opacity: animation, - child: SlideTransition( - position: Tween( - begin: const Offset(0, 0.3), - end: const Offset(0, 0), - ).animate(animation), - child: child, - ), - ); - } - return Scaffold( - body: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Welcome to Vaani', - style: Theme.of(context).textTheme.headlineSmall, + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Center( + child: SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: 600, + minWidth: constraints.maxWidth < 600 + ? constraints.maxWidth + : 0, + ), + child: const Padding( + padding: EdgeInsets.symmetric(vertical: 20.0), + child: SafeArea(child: OnboardingBody()), + ), + ), ), - ), - const SizedBox.square( - dimension: 16.0, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: AnimatedSwitcher( - duration: 500.ms, - transitionBuilder: fadeSlideTransitionBuilder, - child: canUserLogin.value - ? Text( - 'Server connected, please login', - key: const ValueKey('connected'), - style: Theme.of(context).textTheme.bodyMedium, - ) - : Text( - 'Please enter the URL of your AudiobookShelf Server', - key: const ValueKey('not_connected'), - style: Theme.of(context).textTheme.bodyMedium, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: AddNewServer( - controller: serverUriController, - allowEmpty: true, - onPressed: () { - canUserLogin.value = serverUriController.text.isNotEmpty; - }, - ), - ), - AnimatedSwitcher( - duration: 500.ms, - transitionBuilder: fadeSlideTransitionBuilder, - child: canUserLogin.value - ? UserLoginWidget( - server: audiobookshelfUri, - ) - // ).animate().fade(duration: 600.ms).slideY(begin: 0.3, end: 0) - : const RedirectToABS().animate().fadeIn().slideY( - curve: Curves.easeInOut, - duration: 500.ms, - ), - ), - ], + ); + }, ), ); } } +Widget fadeSlideTransitionBuilder(Widget child, Animation animation) { + return FadeTransition( + opacity: animation, + child: SlideTransition( + position: Tween( + begin: const Offset(0, 0.3), + end: const Offset(0, 0), + ).animate(animation), + child: child, + ), + ); +} + +class OnboardingBody extends HookConsumerWidget { + const OnboardingBody({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final apiSettings = ref.watch(apiSettingsProvider); + final serverUriController = useTextEditingController( + text: apiSettings.activeServer?.serverUrl.toString() ?? 'https://', + ); + var audiobookshelfUri = makeBaseUrl(serverUriController.text); + + final canUserLogin = useState(apiSettings.activeServer != null); + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Welcome to Vaani', + style: Theme.of(context).textTheme.headlineSmall, + ), + ), + const SizedBox.square(dimension: 16.0), + Padding( + padding: const EdgeInsets.all(8.0), + child: AnimatedSwitcher( + duration: 500.ms, + transitionBuilder: fadeSlideTransitionBuilder, + child: canUserLogin.value + ? Text( + 'Server connected, please login', + key: const ValueKey('connected'), + style: Theme.of(context).textTheme.bodyMedium, + ) + : Text( + 'Please enter the URL of your AudiobookShelf Server', + key: const ValueKey('not_connected'), + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: AddNewServer( + controller: serverUriController, + allowEmpty: true, + onPressed: () { + canUserLogin.value = serverUriController.text.isNotEmpty; + }, + ), + ), + const SizedBox.square(dimension: 16.0), + AnimatedSwitcher( + duration: 500.ms, + transitionBuilder: fadeSlideTransitionBuilder, + child: canUserLogin.value + ? UserLoginWidget(server: audiobookshelfUri) + // ).animate().fade(duration: 600.ms).slideY(begin: 0.3, end: 0) + : const RedirectToABS().animate().fadeIn().slideY( + curve: Curves.easeInOut, + duration: 500.ms, + ), + ), + ], + ); + } +} + class RedirectToABS extends StatelessWidget { - const RedirectToABS({ - super.key, - }); + const RedirectToABS({super.key}); @override Widget build(BuildContext context) { @@ -119,18 +138,14 @@ class RedirectToABS extends StatelessWidget { isSemanticButton: false, style: ButtonStyle( elevation: WidgetStateProperty.all(0), - padding: WidgetStateProperty.all( - const EdgeInsets.all(0), - ), + padding: WidgetStateProperty.all(const EdgeInsets.all(0)), ), onPressed: () async { // open the github page // ignore: avoid_print print('Opening the github page'); await handleLaunchUrl( - Uri.parse( - 'https://www.audiobookshelf.org', - ), + Uri.parse('https://www.audiobookshelf.org'), ); }, child: const Text('Click here'), diff --git a/lib/features/onboarding/view/user_login.dart b/lib/features/onboarding/view/user_login.dart index 2b3d575..7428953 100644 --- a/lib/features/onboarding/view/user_login.dart +++ b/lib/features/onboarding/view/user_login.dart @@ -1,32 +1,38 @@ import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:shelfsdk/audiobookshelf_api.dart'; -import 'package:vaani/api/api_provider.dart'; -import 'package:vaani/api/server_provider.dart'; -import 'package:vaani/features/onboarding/view/user_login_with_open_id.dart'; -import 'package:vaani/features/onboarding/view/user_login_with_password.dart'; -import 'package:vaani/features/onboarding/view/user_login_with_token.dart'; -import 'package:vaani/hacks/fix_autofill_losing_focus.dart'; -import 'package:vaani/models/error_response.dart'; -import 'package:vaani/settings/api_settings_provider.dart'; +import 'package:shelfsdk/audiobookshelf_api.dart' show AuthMethod; +import 'package:vaani/api/api_provider.dart' show serverStatusProvider; +import 'package:vaani/api/server_provider.dart' + show ServerAlreadyExistsException, audiobookShelfServerProvider; +import 'package:vaani/features/onboarding/view/onboarding_single_page.dart' + show fadeSlideTransitionBuilder; +import 'package:vaani/features/onboarding/view/user_login_with_open_id.dart' + show UserLoginWithOpenID; +import 'package:vaani/features/onboarding/view/user_login_with_password.dart' + show UserLoginWithPassword; +import 'package:vaani/features/onboarding/view/user_login_with_token.dart' + show UserLoginWithToken; +import 'package:vaani/hacks/fix_autofill_losing_focus.dart' + show InactiveFocusScopeObserver; +import 'package:vaani/models/error_response.dart' show ErrorResponseHandler; +import 'package:vaani/settings/api_settings_provider.dart' + show apiSettingsProvider; import 'package:vaani/settings/models/models.dart' as model; class UserLoginWidget extends HookConsumerWidget { - UserLoginWidget({ - super.key, - required this.server, - }); + const UserLoginWidget({super.key, required this.server, this.onSuccess}); final Uri server; - final serverStatusError = ErrorResponseHandler(); + final Function(model.AuthenticatedUser)? onSuccess; @override Widget build(BuildContext context, WidgetRef ref) { - final serverStatus = - ref.watch(serverStatusProvider(server, serverStatusError.storeError)); - - final api = ref.watch(audiobookshelfApiProvider(server)); + final serverStatusError = useMemoized(() => ErrorResponseHandler(), []); + final serverStatus = ref.watch( + serverStatusProvider(server, serverStatusError.storeError), + ); return serverStatus.when( data: (value) { @@ -42,12 +48,11 @@ class UserLoginWidget extends HookConsumerWidget { openIDAvailable: value.authMethods?.contains(AuthMethod.openid) ?? false, openIDButtonText: value.authFormData?.authOpenIDButtonText, + onSuccess: onSuccess, ); }, loading: () { - return const Center( - child: CircularProgressIndicator(), - ); + return const Center(child: CircularProgressIndicator()); }, error: (error, _) { return Center( @@ -58,10 +63,7 @@ class UserLoginWidget extends HookConsumerWidget { ElevatedButton( onPressed: () { ref.invalidate( - serverStatusProvider( - server, - serverStatusError.storeError, - ), + serverStatusProvider(server, serverStatusError.storeError), ); }, child: const Text('Try again'), @@ -74,11 +76,7 @@ class UserLoginWidget extends HookConsumerWidget { } } -enum AuthMethodChoice { - local, - openid, - authToken, -} +enum AuthMethodChoice { local, openid, authToken } class UserLoginMultipleAuth extends HookConsumerWidget { const UserLoginMultipleAuth({ @@ -88,6 +86,7 @@ class UserLoginMultipleAuth extends HookConsumerWidget { this.openIDAvailable = false, this.onPressed, this.openIDButtonText, + this.onSuccess, }); final Uri server; @@ -95,6 +94,7 @@ class UserLoginMultipleAuth extends HookConsumerWidget { final bool openIDAvailable; final void Function()? onPressed; final String? openIDButtonText; + final Function(model.AuthenticatedUser)? onSuccess; @override Widget build(BuildContext context, WidgetRef ref) { @@ -104,24 +104,18 @@ class UserLoginMultipleAuth extends HookConsumerWidget { localAvailable ? AuthMethodChoice.local : AuthMethodChoice.authToken, ); - final apiSettings = ref.watch(apiSettingsProvider); - model.AudiobookShelfServer addServer() { - var newServer = model.AudiobookShelfServer( - serverUrl: server, - ); + var newServer = model.AudiobookShelfServer(serverUrl: server); try { // add the server to the list of servers - ref.read(audiobookShelfServerProvider.notifier).addServer( - newServer, - ); + ref.read(audiobookShelfServerProvider.notifier).addServer(newServer); } on ServerAlreadyExistsException catch (e) { newServer = e.server; } finally { - ref.read(apiSettingsProvider.notifier).updateState( - apiSettings.copyWith( - activeServer: newServer, - ), + ref + .read(apiSettingsProvider.notifier) + .updateState( + ref.read(apiSettingsProvider).copyWith(activeServer: newServer), ); } return newServer; @@ -130,70 +124,87 @@ class UserLoginMultipleAuth extends HookConsumerWidget { return Center( child: InactiveFocusScopeObserver( child: AutofillGroup( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - Wrap( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Wrap( // mainAxisAlignment: MainAxisAlignment.center, spacing: 10, runAlignment: WrapAlignment.center, runSpacing: 10, alignment: WrapAlignment.center, - children: [ - // a small label to show the user what to do - if (localAvailable) - ChoiceChip( - label: const Text('Local'), - selected: methodChoice.value == AuthMethodChoice.local, - onSelected: (selected) { - if (selected) { - methodChoice.value = AuthMethodChoice.local; - } - }, - ), - if (openIDAvailable) - ChoiceChip( - label: const Text('OpenID'), - selected: methodChoice.value == AuthMethodChoice.openid, - onSelected: (selected) { - if (selected) { - methodChoice.value = AuthMethodChoice.openid; - } - }, - ), - ChoiceChip( - label: const Text('Token'), - selected: - methodChoice.value == AuthMethodChoice.authToken, - onSelected: (selected) { - if (selected) { - methodChoice.value = AuthMethodChoice.authToken; - } - }, - ), - ], + children: + [ + // a small label to show the user what to do + if (localAvailable) + ChoiceChip( + label: const Text('Local'), + selected: + methodChoice.value == + AuthMethodChoice.local, + onSelected: (selected) { + if (selected) { + methodChoice.value = AuthMethodChoice.local; + } + }, + ), + if (openIDAvailable) + ChoiceChip( + label: const Text('OpenID'), + selected: + methodChoice.value == + AuthMethodChoice.openid, + onSelected: (selected) { + if (selected) { + methodChoice.value = + AuthMethodChoice.openid; + } + }, + ), + ChoiceChip( + label: const Text('Token'), + selected: + methodChoice.value == + AuthMethodChoice.authToken, + onSelected: (selected) { + if (selected) { + methodChoice.value = + AuthMethodChoice.authToken; + } + }, + ), + ] + .animate(interval: 100.ms) + .fadeIn(duration: 150.ms, curve: Curves.easeIn), ), - const SizedBox.square( - dimension: 8, - ), - switch (methodChoice.value) { - AuthMethodChoice.authToken => UserLoginWithToken( + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: AnimatedSwitcher( + duration: 200.ms, + transitionBuilder: fadeSlideTransitionBuilder, + child: switch (methodChoice.value) { + AuthMethodChoice.authToken => UserLoginWithToken( server: server, addServer: addServer, + onSuccess: onSuccess, ), - AuthMethodChoice.local => UserLoginWithPassword( + AuthMethodChoice.local => UserLoginWithPassword( server: server, addServer: addServer, + onSuccess: onSuccess, ), - AuthMethodChoice.openid => UserLoginWithOpenID( + AuthMethodChoice.openid => UserLoginWithOpenID( server: server, addServer: addServer, openIDButtonText: openIDButtonText, + onSuccess: onSuccess, ), - }, - ], - ), + }, + ), + ), + ], ), ), ), diff --git a/lib/features/onboarding/view/user_login_with_open_id.dart b/lib/features/onboarding/view/user_login_with_open_id.dart index e403c81..339b6c5 100644 --- a/lib/features/onboarding/view/user_login_with_open_id.dart +++ b/lib/features/onboarding/view/user_login_with_open_id.dart @@ -11,6 +11,7 @@ import 'package:vaani/models/error_response.dart'; import 'package:vaani/router/router.dart'; import 'package:vaani/settings/constants.dart'; import 'package:vaani/settings/models/models.dart' as model; +import 'package:vaani/shared/extensions/obfuscation.dart'; import 'package:vaani/shared/utils.dart'; class UserLoginWithOpenID extends HookConsumerWidget { @@ -19,12 +20,14 @@ class UserLoginWithOpenID extends HookConsumerWidget { required this.server, required this.addServer, this.openIDButtonText, + this.onSuccess, }); final Uri server; final model.AudiobookShelfServer Function() addServer; final String? openIDButtonText; final responseErrorHandler = ErrorResponseHandler(name: 'OpenID'); + final Function(model.AuthenticatedUser)? onSuccess; @override Widget build(BuildContext context, WidgetRef ref) { @@ -51,9 +54,9 @@ class UserLoginWithOpenID extends HookConsumerWidget { if (openIDLoginEndpoint == null) { if (responseErrorHandler.response.statusCode == 400 && - responseErrorHandler.response.body - .toLowerCase() - .contains(RegExp(r'invalid.*redirect.*uri'))) { + responseErrorHandler.response.body.toLowerCase().contains( + RegExp(r'invalid.*redirect.*uri'), + )) { // show error handleServerError( context, @@ -89,19 +92,21 @@ class UserLoginWithOpenID extends HookConsumerWidget { return; } - appLogger.fine('Got OpenID login endpoint: $openIDLoginEndpoint'); + appLogger.fine( + 'Got OpenID login endpoint: ${openIDLoginEndpoint.obfuscate()}', + ); // add the flow to the provider - ref.read(oauthFlowsProvider.notifier).addFlow( + ref + .read(oauthFlowsProvider.notifier) + .addFlow( oauthState, verifier: verifier, serverUri: server, cookie: Cookie.fromSetCookieValue(authCookie!), ); - await handleLaunchUrl( - openIDLoginEndpoint, - ); + await handleLaunchUrl(openIDLoginEndpoint); } return Column( diff --git a/lib/features/onboarding/view/user_login_with_password.dart b/lib/features/onboarding/view/user_login_with_password.dart index 91eeea0..20d2613 100644 --- a/lib/features/onboarding/view/user_login_with_password.dart +++ b/lib/features/onboarding/view/user_login_with_password.dart @@ -5,10 +5,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:lottie/lottie.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:vaani/api/api_provider.dart'; -import 'package:vaani/api/authenticated_user_provider.dart'; +import 'package:vaani/api/authenticated_users_provider.dart'; import 'package:vaani/hacks/fix_autofill_losing_focus.dart'; import 'package:vaani/models/error_response.dart'; import 'package:vaani/router/router.dart'; +import 'package:vaani/settings/constants.dart'; import 'package:vaani/settings/models/models.dart' as model; import 'package:vaani/shared/utils.dart'; @@ -17,17 +18,20 @@ class UserLoginWithPassword extends HookConsumerWidget { super.key, required this.server, required this.addServer, + this.onSuccess, }); final Uri server; final model.AudiobookShelfServer Function() addServer; final serverErrorResponse = ErrorResponseHandler(); + final Function(model.AuthenticatedUser)? onSuccess; @override Widget build(BuildContext context, WidgetRef ref) { final usernameController = useTextEditingController(); final passwordController = useTextEditingController(); final isPasswordVisibleAnimationController = useAnimationController( + initialValue: 1, duration: const Duration(milliseconds: 500), ); @@ -35,17 +39,14 @@ class UserLoginWithPassword extends HookConsumerWidget { final api = ref.watch(audiobookshelfApiProvider(server)); // forward animation when the password visibility changes - useEffect( - () { - if (isPasswordVisible.value) { - isPasswordVisibleAnimationController.forward(); - } else { - isPasswordVisibleAnimationController.reverse(); - } - return null; - }, - [isPasswordVisible.value], - ); + useEffect(() { + if (isPasswordVisible.value) { + isPasswordVisibleAnimationController.forward(); + } else { + isPasswordVisibleAnimationController.reverse(); + } + return null; + }, [isPasswordVisible.value]); /// Login to the server and save the user Future loginAndSave() async { @@ -76,92 +77,89 @@ class UserLoginWithPassword extends HookConsumerWidget { final authenticatedUser = model.AuthenticatedUser( server: addServer(), id: success.user.id, - password: password, username: username, authToken: api.token!, ); - // add the user to the list of users - ref - .read(authenticatedUserProvider.notifier) - .addUser(authenticatedUser, setActive: true); - // redirect to the library page - GoRouter.of(context).goNamed(Routes.home.name); + if (onSuccess != null) { + onSuccess!(authenticatedUser); + } else { + // add the user to the list of users + ref + .read(authenticatedUsersProvider.notifier) + .addUser(authenticatedUser, setActive: true); + context.goNamed(Routes.home.name); + } } return Center( child: InactiveFocusScopeObserver( child: AutofillGroup( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextFormField( - controller: usernameController, - autofocus: true, - autofillHints: const [AutofillHints.username], - textInputAction: TextInputAction.next, - decoration: InputDecoration( - labelText: 'Username', - labelStyle: TextStyle( - color: Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.8), - ), - border: const OutlineInputBorder(), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextFormField( + controller: usernameController, + autofocus: true, + autofillHints: const [AutofillHints.username], + textInputAction: TextInputAction.next, + decoration: InputDecoration( + labelText: 'Username', + labelStyle: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.8), ), + border: const OutlineInputBorder(), ), - const SizedBox(height: 10), - TextFormField( - controller: passwordController, - autofillHints: const [AutofillHints.password], - textInputAction: TextInputAction.done, - obscureText: !isPasswordVisible.value, - onFieldSubmitted: (_) { - loginAndSave(); - }, - decoration: InputDecoration( - labelText: 'Password', - labelStyle: TextStyle( - color: Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.8), + ), + const SizedBox(height: 10), + TextFormField( + controller: passwordController, + autofillHints: const [AutofillHints.password], + textInputAction: TextInputAction.done, + obscureText: !isPasswordVisible.value, + onFieldSubmitted: (_) { + loginAndSave(); + }, + decoration: InputDecoration( + labelText: 'Password', + labelStyle: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.8), + ), + border: const OutlineInputBorder(), + suffixIcon: ColorFiltered( + colorFilter: ColorFilter.mode( + Theme.of( + context, + ).colorScheme.primary.withValues(alpha: 0.8), + BlendMode.srcIn, ), - border: const OutlineInputBorder(), - suffixIcon: ColorFiltered( - colorFilter: ColorFilter.mode( - Theme.of(context).colorScheme.primary.withOpacity(0.8), - BlendMode.srcIn, - ), - child: InkWell( - borderRadius: BorderRadius.circular(50), - onTap: () { - isPasswordVisible.value = !isPasswordVisible.value; - }, - child: Container( - margin: const EdgeInsets.only(left: 8, right: 8), - child: Lottie.asset( - 'assets/animations/Animation - 1714930099660.json', - controller: isPasswordVisibleAnimationController, - ), + child: InkWell( + borderRadius: BorderRadius.circular(50), + onTap: () { + isPasswordVisible.value = !isPasswordVisible.value; + }, + child: Container( + margin: const EdgeInsets.only(left: 8, right: 8), + child: Lottie.asset( + 'assets/animations/Animation - 1714930099660.json', + controller: isPasswordVisibleAnimationController, ), ), ), - suffixIconConstraints: const BoxConstraints( - maxHeight: 45, - ), ), + suffixIconConstraints: const BoxConstraints(maxHeight: 45), ), - const SizedBox(height: 30), - ElevatedButton( - onPressed: loginAndSave, - child: const Text('Login'), - ), - ], - ), + ), + const SizedBox(height: 30), + ElevatedButton( + onPressed: loginAndSave, + child: const Text('Login'), + ), + ], ), ), ), @@ -191,10 +189,12 @@ Future handleServerError( context: context, builder: (context) => AlertDialog( title: const Text('Error'), - content: SelectableText('$title\n' - 'Got response: ${responseErrorHandler.response.body} (${responseErrorHandler.response.statusCode})\n' - 'Stacktrace: $e\n\n' - '$body\n\n'), + content: SelectableText( + '$title\n' + 'Got response: ${responseErrorHandler.response.body} (${responseErrorHandler.response.statusCode})\n' + 'Stacktrace: $e\n\n' + '$body\n\n', + ), actions: [ if (outLink != null) TextButton( @@ -207,8 +207,10 @@ Future handleServerError( onPressed: () { // open an issue on the github page handleLaunchUrl( - Uri.parse( - 'https://github.com/Dr-Blank/Vaani/issues', + AppMetadata.githubRepo + // append the issue url + .replace( + path: '${AppMetadata.githubRepo.path}/issues/new', ), ); }, diff --git a/lib/features/onboarding/view/user_login_with_token.dart b/lib/features/onboarding/view/user_login_with_token.dart index 35cdf55..fb5a1fa 100644 --- a/lib/features/onboarding/view/user_login_with_token.dart +++ b/lib/features/onboarding/view/user_login_with_token.dart @@ -4,7 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:vaani/api/api_provider.dart'; -import 'package:vaani/api/authenticated_user_provider.dart'; +import 'package:vaani/api/authenticated_users_provider.dart'; import 'package:vaani/models/error_response.dart'; import 'package:vaani/router/router.dart'; import 'package:vaani/settings/models/models.dart' as model; @@ -14,11 +14,13 @@ class UserLoginWithToken extends HookConsumerWidget { super.key, required this.server, required this.addServer, + this.onSuccess, }); final Uri server; final model.AudiobookShelfServer Function() addServer; final serverErrorResponse = ErrorResponseHandler(); + final Function(model.AuthenticatedUser)? onSuccess; @override Widget build(BuildContext context, WidgetRef ref) { @@ -65,11 +67,14 @@ class UserLoginWithToken extends HookConsumerWidget { authToken: api.token!, ); - ref - .read(authenticatedUserProvider.notifier) - .addUser(authenticatedUser, setActive: true); - - context.goNamed(Routes.home.name); + if (onSuccess != null) { + onSuccess!(authenticatedUser); + } else { + ref + .read(authenticatedUsersProvider.notifier) + .addUser(authenticatedUser, setActive: true); + context.goNamed(Routes.home.name); + } } return Form( @@ -84,7 +89,9 @@ class UserLoginWithToken extends HookConsumerWidget { decoration: InputDecoration( labelText: 'API Token', labelStyle: TextStyle( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8), + color: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.8), ), border: const OutlineInputBorder(), ), @@ -99,10 +106,7 @@ class UserLoginWithToken extends HookConsumerWidget { }, ), const SizedBox(height: 10), - ElevatedButton( - onPressed: loginAndSave, - child: const Text('Login'), - ), + ElevatedButton(onPressed: loginAndSave, child: const Text('Login')), ], ), ); diff --git a/lib/features/per_book_settings/models/book_settings.dart b/lib/features/per_book_settings/models/book_settings.dart index b4f0cda..853fe53 100644 --- a/lib/features/per_book_settings/models/book_settings.dart +++ b/lib/features/per_book_settings/models/book_settings.dart @@ -6,7 +6,7 @@ part 'book_settings.g.dart'; /// per book settings @freezed -class BookSettings with _$BookSettings { +sealed class BookSettings with _$BookSettings { const factory BookSettings({ required String bookId, @Default(NullablePlayerSettings()) NullablePlayerSettings playerSettings, diff --git a/lib/features/per_book_settings/models/book_settings.freezed.dart b/lib/features/per_book_settings/models/book_settings.freezed.dart index 63b55da..8ba7009 100644 --- a/lib/features/per_book_settings/models/book_settings.freezed.dart +++ b/lib/features/per_book_settings/models/book_settings.freezed.dart @@ -1,5 +1,5 @@ -// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark @@ -9,195 +9,284 @@ part of 'book_settings.dart'; // FreezedGenerator // ************************************************************************** +// dart format off T _$identity(T value) => value; -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -BookSettings _$BookSettingsFromJson(Map json) { - return _BookSettings.fromJson(json); -} - /// @nodoc mixin _$BookSettings { - String get bookId => throw _privateConstructorUsedError; - NullablePlayerSettings get playerSettings => - throw _privateConstructorUsedError; + + String get bookId; NullablePlayerSettings get playerSettings; +/// Create a copy of BookSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$BookSettingsCopyWith get copyWith => _$BookSettingsCopyWithImpl(this as BookSettings, _$identity); /// Serializes this BookSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is BookSettings&&(identical(other.bookId, bookId) || other.bookId == bookId)&&(identical(other.playerSettings, playerSettings) || other.playerSettings == playerSettings)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,bookId,playerSettings); + +@override +String toString() { + return 'BookSettings(bookId: $bookId, playerSettings: $playerSettings)'; +} + - /// Create a copy of BookSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $BookSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $BookSettingsCopyWith<$Res> { - factory $BookSettingsCopyWith( - BookSettings value, $Res Function(BookSettings) then) = - _$BookSettingsCopyWithImpl<$Res, BookSettings>; - @useResult - $Res call({String bookId, NullablePlayerSettings playerSettings}); +abstract mixin class $BookSettingsCopyWith<$Res> { + factory $BookSettingsCopyWith(BookSettings value, $Res Function(BookSettings) _then) = _$BookSettingsCopyWithImpl; +@useResult +$Res call({ + String bookId, NullablePlayerSettings playerSettings +}); + + +$NullablePlayerSettingsCopyWith<$Res> get playerSettings; - $NullablePlayerSettingsCopyWith<$Res> get playerSettings; } - /// @nodoc -class _$BookSettingsCopyWithImpl<$Res, $Val extends BookSettings> +class _$BookSettingsCopyWithImpl<$Res> implements $BookSettingsCopyWith<$Res> { - _$BookSettingsCopyWithImpl(this._value, this._then); + _$BookSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final BookSettings _self; + final $Res Function(BookSettings) _then; - /// Create a copy of BookSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? bookId = null, - Object? playerSettings = null, - }) { - return _then(_value.copyWith( - bookId: null == bookId - ? _value.bookId - : bookId // ignore: cast_nullable_to_non_nullable - as String, - playerSettings: null == playerSettings - ? _value.playerSettings - : playerSettings // ignore: cast_nullable_to_non_nullable - as NullablePlayerSettings, - ) as $Val); - } - - /// Create a copy of BookSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $NullablePlayerSettingsCopyWith<$Res> get playerSettings { - return $NullablePlayerSettingsCopyWith<$Res>(_value.playerSettings, - (value) { - return _then(_value.copyWith(playerSettings: value) as $Val); - }); - } +/// Create a copy of BookSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? bookId = null,Object? playerSettings = null,}) { + return _then(_self.copyWith( +bookId: null == bookId ? _self.bookId : bookId // ignore: cast_nullable_to_non_nullable +as String,playerSettings: null == playerSettings ? _self.playerSettings : playerSettings // ignore: cast_nullable_to_non_nullable +as NullablePlayerSettings, + )); +} +/// Create a copy of BookSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NullablePlayerSettingsCopyWith<$Res> get playerSettings { + + return $NullablePlayerSettingsCopyWith<$Res>(_self.playerSettings, (value) { + return _then(_self.copyWith(playerSettings: value)); + }); +} } -/// @nodoc -abstract class _$$BookSettingsImplCopyWith<$Res> - implements $BookSettingsCopyWith<$Res> { - factory _$$BookSettingsImplCopyWith( - _$BookSettingsImpl value, $Res Function(_$BookSettingsImpl) then) = - __$$BookSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({String bookId, NullablePlayerSettings playerSettings}); - @override - $NullablePlayerSettingsCopyWith<$Res> get playerSettings; +/// Adds pattern-matching-related methods to [BookSettings]. +extension BookSettingsPatterns on BookSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _BookSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _BookSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _BookSettings value) $default,){ +final _that = this; +switch (_that) { +case _BookSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _BookSettings value)? $default,){ +final _that = this; +switch (_that) { +case _BookSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String bookId, NullablePlayerSettings playerSettings)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _BookSettings() when $default != null: +return $default(_that.bookId,_that.playerSettings);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String bookId, NullablePlayerSettings playerSettings) $default,) {final _that = this; +switch (_that) { +case _BookSettings(): +return $default(_that.bookId,_that.playerSettings);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String bookId, NullablePlayerSettings playerSettings)? $default,) {final _that = this; +switch (_that) { +case _BookSettings() when $default != null: +return $default(_that.bookId,_that.playerSettings);case _: + return null; + +} } -/// @nodoc -class __$$BookSettingsImplCopyWithImpl<$Res> - extends _$BookSettingsCopyWithImpl<$Res, _$BookSettingsImpl> - implements _$$BookSettingsImplCopyWith<$Res> { - __$$BookSettingsImplCopyWithImpl( - _$BookSettingsImpl _value, $Res Function(_$BookSettingsImpl) _then) - : super(_value, _then); - - /// Create a copy of BookSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? bookId = null, - Object? playerSettings = null, - }) { - return _then(_$BookSettingsImpl( - bookId: null == bookId - ? _value.bookId - : bookId // ignore: cast_nullable_to_non_nullable - as String, - playerSettings: null == playerSettings - ? _value.playerSettings - : playerSettings // ignore: cast_nullable_to_non_nullable - as NullablePlayerSettings, - )); - } } /// @nodoc @JsonSerializable() -class _$BookSettingsImpl implements _BookSettings { - const _$BookSettingsImpl( - {required this.bookId, - this.playerSettings = const NullablePlayerSettings()}); - factory _$BookSettingsImpl.fromJson(Map json) => - _$$BookSettingsImplFromJson(json); +class _BookSettings implements BookSettings { + const _BookSettings({required this.bookId, this.playerSettings = const NullablePlayerSettings()}); + factory _BookSettings.fromJson(Map json) => _$BookSettingsFromJson(json); - @override - final String bookId; - @override - @JsonKey() - final NullablePlayerSettings playerSettings; +@override final String bookId; +@override@JsonKey() final NullablePlayerSettings playerSettings; - @override - String toString() { - return 'BookSettings(bookId: $bookId, playerSettings: $playerSettings)'; - } +/// Create a copy of BookSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$BookSettingsCopyWith<_BookSettings> get copyWith => __$BookSettingsCopyWithImpl<_BookSettings>(this, _$identity); - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$BookSettingsImpl && - (identical(other.bookId, bookId) || other.bookId == bookId) && - (identical(other.playerSettings, playerSettings) || - other.playerSettings == playerSettings)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, bookId, playerSettings); - - /// Create a copy of BookSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$BookSettingsImplCopyWith<_$BookSettingsImpl> get copyWith => - __$$BookSettingsImplCopyWithImpl<_$BookSettingsImpl>(this, _$identity); - - @override - Map toJson() { - return _$$BookSettingsImplToJson( - this, - ); - } +@override +Map toJson() { + return _$BookSettingsToJson(this, ); } -abstract class _BookSettings implements BookSettings { - const factory _BookSettings( - {required final String bookId, - final NullablePlayerSettings playerSettings}) = _$BookSettingsImpl; - - factory _BookSettings.fromJson(Map json) = - _$BookSettingsImpl.fromJson; - - @override - String get bookId; - @override - NullablePlayerSettings get playerSettings; - - /// Create a copy of BookSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$BookSettingsImplCopyWith<_$BookSettingsImpl> get copyWith => - throw _privateConstructorUsedError; +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _BookSettings&&(identical(other.bookId, bookId) || other.bookId == bookId)&&(identical(other.playerSettings, playerSettings) || other.playerSettings == playerSettings)); } + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,bookId,playerSettings); + +@override +String toString() { + return 'BookSettings(bookId: $bookId, playerSettings: $playerSettings)'; +} + + +} + +/// @nodoc +abstract mixin class _$BookSettingsCopyWith<$Res> implements $BookSettingsCopyWith<$Res> { + factory _$BookSettingsCopyWith(_BookSettings value, $Res Function(_BookSettings) _then) = __$BookSettingsCopyWithImpl; +@override @useResult +$Res call({ + String bookId, NullablePlayerSettings playerSettings +}); + + +@override $NullablePlayerSettingsCopyWith<$Res> get playerSettings; + +} +/// @nodoc +class __$BookSettingsCopyWithImpl<$Res> + implements _$BookSettingsCopyWith<$Res> { + __$BookSettingsCopyWithImpl(this._self, this._then); + + final _BookSettings _self; + final $Res Function(_BookSettings) _then; + +/// Create a copy of BookSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? bookId = null,Object? playerSettings = null,}) { + return _then(_BookSettings( +bookId: null == bookId ? _self.bookId : bookId // ignore: cast_nullable_to_non_nullable +as String,playerSettings: null == playerSettings ? _self.playerSettings : playerSettings // ignore: cast_nullable_to_non_nullable +as NullablePlayerSettings, + )); +} + +/// Create a copy of BookSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NullablePlayerSettingsCopyWith<$Res> get playerSettings { + + return $NullablePlayerSettingsCopyWith<$Res>(_self.playerSettings, (value) { + return _then(_self.copyWith(playerSettings: value)); + }); +} +} + +// dart format on diff --git a/lib/features/per_book_settings/models/book_settings.g.dart b/lib/features/per_book_settings/models/book_settings.g.dart index a66fba1..b5888f7 100644 --- a/lib/features/per_book_settings/models/book_settings.g.dart +++ b/lib/features/per_book_settings/models/book_settings.g.dart @@ -6,16 +6,17 @@ part of 'book_settings.dart'; // JsonSerializableGenerator // ************************************************************************** -_$BookSettingsImpl _$$BookSettingsImplFromJson(Map json) => - _$BookSettingsImpl( +_BookSettings _$BookSettingsFromJson(Map json) => + _BookSettings( bookId: json['bookId'] as String, playerSettings: json['playerSettings'] == null ? const NullablePlayerSettings() : NullablePlayerSettings.fromJson( - json['playerSettings'] as Map), + json['playerSettings'] as Map, + ), ); -Map _$$BookSettingsImplToJson(_$BookSettingsImpl instance) => +Map _$BookSettingsToJson(_BookSettings instance) => { 'bookId': instance.bookId, 'playerSettings': instance.playerSettings, diff --git a/lib/features/per_book_settings/models/nullable_player_settings.dart b/lib/features/per_book_settings/models/nullable_player_settings.dart index 39135d7..d54e2eb 100644 --- a/lib/features/per_book_settings/models/nullable_player_settings.dart +++ b/lib/features/per_book_settings/models/nullable_player_settings.dart @@ -5,7 +5,7 @@ part 'nullable_player_settings.freezed.dart'; part 'nullable_player_settings.g.dart'; @freezed -class NullablePlayerSettings with _$NullablePlayerSettings { +sealed class NullablePlayerSettings with _$NullablePlayerSettings { const factory NullablePlayerSettings({ MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, diff --git a/lib/features/per_book_settings/models/nullable_player_settings.freezed.dart b/lib/features/per_book_settings/models/nullable_player_settings.freezed.dart index 9450ed4..8039d60 100644 --- a/lib/features/per_book_settings/models/nullable_player_settings.freezed.dart +++ b/lib/features/per_book_settings/models/nullable_player_settings.freezed.dart @@ -1,5 +1,5 @@ -// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark @@ -9,369 +9,361 @@ part of 'nullable_player_settings.dart'; // FreezedGenerator // ************************************************************************** +// dart format off T _$identity(T value) => value; -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -NullablePlayerSettings _$NullablePlayerSettingsFromJson( - Map json) { - return _NullablePlayerSettings.fromJson(json); -} - /// @nodoc mixin _$NullablePlayerSettings { - MinimizedPlayerSettings? get miniPlayerSettings => - throw _privateConstructorUsedError; - ExpandedPlayerSettings? get expandedPlayerSettings => - throw _privateConstructorUsedError; - double? get preferredDefaultVolume => throw _privateConstructorUsedError; - double? get preferredDefaultSpeed => throw _privateConstructorUsedError; - List? get speedOptions => throw _privateConstructorUsedError; - SleepTimerSettings? get sleepTimerSettings => - throw _privateConstructorUsedError; - Duration? get playbackReportInterval => throw _privateConstructorUsedError; + + MinimizedPlayerSettings? get miniPlayerSettings; ExpandedPlayerSettings? get expandedPlayerSettings; double? get preferredDefaultVolume; double? get preferredDefaultSpeed; List? get speedOptions; SleepTimerSettings? get sleepTimerSettings; Duration? get playbackReportInterval; +/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$NullablePlayerSettingsCopyWith get copyWith => _$NullablePlayerSettingsCopyWithImpl(this as NullablePlayerSettings, _$identity); /// Serializes this NullablePlayerSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is NullablePlayerSettings&&(identical(other.miniPlayerSettings, miniPlayerSettings) || other.miniPlayerSettings == miniPlayerSettings)&&(identical(other.expandedPlayerSettings, expandedPlayerSettings) || other.expandedPlayerSettings == expandedPlayerSettings)&&(identical(other.preferredDefaultVolume, preferredDefaultVolume) || other.preferredDefaultVolume == preferredDefaultVolume)&&(identical(other.preferredDefaultSpeed, preferredDefaultSpeed) || other.preferredDefaultSpeed == preferredDefaultSpeed)&&const DeepCollectionEquality().equals(other.speedOptions, speedOptions)&&(identical(other.sleepTimerSettings, sleepTimerSettings) || other.sleepTimerSettings == sleepTimerSettings)&&(identical(other.playbackReportInterval, playbackReportInterval) || other.playbackReportInterval == playbackReportInterval)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,miniPlayerSettings,expandedPlayerSettings,preferredDefaultVolume,preferredDefaultSpeed,const DeepCollectionEquality().hash(speedOptions),sleepTimerSettings,playbackReportInterval); + +@override +String toString() { + return 'NullablePlayerSettings(miniPlayerSettings: $miniPlayerSettings, expandedPlayerSettings: $expandedPlayerSettings, preferredDefaultVolume: $preferredDefaultVolume, preferredDefaultSpeed: $preferredDefaultSpeed, speedOptions: $speedOptions, sleepTimerSettings: $sleepTimerSettings, playbackReportInterval: $playbackReportInterval)'; +} + - /// Create a copy of NullablePlayerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $NullablePlayerSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $NullablePlayerSettingsCopyWith<$Res> { - factory $NullablePlayerSettingsCopyWith(NullablePlayerSettings value, - $Res Function(NullablePlayerSettings) then) = - _$NullablePlayerSettingsCopyWithImpl<$Res, NullablePlayerSettings>; - @useResult - $Res call( - {MinimizedPlayerSettings? miniPlayerSettings, - ExpandedPlayerSettings? expandedPlayerSettings, - double? preferredDefaultVolume, - double? preferredDefaultSpeed, - List? speedOptions, - SleepTimerSettings? sleepTimerSettings, - Duration? playbackReportInterval}); +abstract mixin class $NullablePlayerSettingsCopyWith<$Res> { + factory $NullablePlayerSettingsCopyWith(NullablePlayerSettings value, $Res Function(NullablePlayerSettings) _then) = _$NullablePlayerSettingsCopyWithImpl; +@useResult +$Res call({ + MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List? speedOptions, SleepTimerSettings? sleepTimerSettings, Duration? playbackReportInterval +}); + + +$MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings;$ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings;$SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings; - $MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings; - $ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings; - $SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings; } - /// @nodoc -class _$NullablePlayerSettingsCopyWithImpl<$Res, - $Val extends NullablePlayerSettings> +class _$NullablePlayerSettingsCopyWithImpl<$Res> implements $NullablePlayerSettingsCopyWith<$Res> { - _$NullablePlayerSettingsCopyWithImpl(this._value, this._then); + _$NullablePlayerSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final NullablePlayerSettings _self; + final $Res Function(NullablePlayerSettings) _then; - /// Create a copy of NullablePlayerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? miniPlayerSettings = freezed, - Object? expandedPlayerSettings = freezed, - Object? preferredDefaultVolume = freezed, - Object? preferredDefaultSpeed = freezed, - Object? speedOptions = freezed, - Object? sleepTimerSettings = freezed, - Object? playbackReportInterval = freezed, - }) { - return _then(_value.copyWith( - miniPlayerSettings: freezed == miniPlayerSettings - ? _value.miniPlayerSettings - : miniPlayerSettings // ignore: cast_nullable_to_non_nullable - as MinimizedPlayerSettings?, - expandedPlayerSettings: freezed == expandedPlayerSettings - ? _value.expandedPlayerSettings - : expandedPlayerSettings // ignore: cast_nullable_to_non_nullable - as ExpandedPlayerSettings?, - preferredDefaultVolume: freezed == preferredDefaultVolume - ? _value.preferredDefaultVolume - : preferredDefaultVolume // ignore: cast_nullable_to_non_nullable - as double?, - preferredDefaultSpeed: freezed == preferredDefaultSpeed - ? _value.preferredDefaultSpeed - : preferredDefaultSpeed // ignore: cast_nullable_to_non_nullable - as double?, - speedOptions: freezed == speedOptions - ? _value.speedOptions - : speedOptions // ignore: cast_nullable_to_non_nullable - as List?, - sleepTimerSettings: freezed == sleepTimerSettings - ? _value.sleepTimerSettings - : sleepTimerSettings // ignore: cast_nullable_to_non_nullable - as SleepTimerSettings?, - playbackReportInterval: freezed == playbackReportInterval - ? _value.playbackReportInterval - : playbackReportInterval // ignore: cast_nullable_to_non_nullable - as Duration?, - ) as $Val); +/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? miniPlayerSettings = freezed,Object? expandedPlayerSettings = freezed,Object? preferredDefaultVolume = freezed,Object? preferredDefaultSpeed = freezed,Object? speedOptions = freezed,Object? sleepTimerSettings = freezed,Object? playbackReportInterval = freezed,}) { + return _then(_self.copyWith( +miniPlayerSettings: freezed == miniPlayerSettings ? _self.miniPlayerSettings : miniPlayerSettings // ignore: cast_nullable_to_non_nullable +as MinimizedPlayerSettings?,expandedPlayerSettings: freezed == expandedPlayerSettings ? _self.expandedPlayerSettings : expandedPlayerSettings // ignore: cast_nullable_to_non_nullable +as ExpandedPlayerSettings?,preferredDefaultVolume: freezed == preferredDefaultVolume ? _self.preferredDefaultVolume : preferredDefaultVolume // ignore: cast_nullable_to_non_nullable +as double?,preferredDefaultSpeed: freezed == preferredDefaultSpeed ? _self.preferredDefaultSpeed : preferredDefaultSpeed // ignore: cast_nullable_to_non_nullable +as double?,speedOptions: freezed == speedOptions ? _self.speedOptions : speedOptions // ignore: cast_nullable_to_non_nullable +as List?,sleepTimerSettings: freezed == sleepTimerSettings ? _self.sleepTimerSettings : sleepTimerSettings // ignore: cast_nullable_to_non_nullable +as SleepTimerSettings?,playbackReportInterval: freezed == playbackReportInterval ? _self.playbackReportInterval : playbackReportInterval // ignore: cast_nullable_to_non_nullable +as Duration?, + )); +} +/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings { + if (_self.miniPlayerSettings == null) { + return null; } - /// Create a copy of NullablePlayerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings { - if (_value.miniPlayerSettings == null) { - return null; - } - - return $MinimizedPlayerSettingsCopyWith<$Res>(_value.miniPlayerSettings!, - (value) { - return _then(_value.copyWith(miniPlayerSettings: value) as $Val); - }); + return $MinimizedPlayerSettingsCopyWith<$Res>(_self.miniPlayerSettings!, (value) { + return _then(_self.copyWith(miniPlayerSettings: value)); + }); +}/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings { + if (_self.expandedPlayerSettings == null) { + return null; } - /// Create a copy of NullablePlayerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings { - if (_value.expandedPlayerSettings == null) { - return null; - } - - return $ExpandedPlayerSettingsCopyWith<$Res>(_value.expandedPlayerSettings!, - (value) { - return _then(_value.copyWith(expandedPlayerSettings: value) as $Val); - }); + return $ExpandedPlayerSettingsCopyWith<$Res>(_self.expandedPlayerSettings!, (value) { + return _then(_self.copyWith(expandedPlayerSettings: value)); + }); +}/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings { + if (_self.sleepTimerSettings == null) { + return null; } - /// Create a copy of NullablePlayerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings { - if (_value.sleepTimerSettings == null) { - return null; - } - - return $SleepTimerSettingsCopyWith<$Res>(_value.sleepTimerSettings!, - (value) { - return _then(_value.copyWith(sleepTimerSettings: value) as $Val); - }); - } + return $SleepTimerSettingsCopyWith<$Res>(_self.sleepTimerSettings!, (value) { + return _then(_self.copyWith(sleepTimerSettings: value)); + }); +} } -/// @nodoc -abstract class _$$NullablePlayerSettingsImplCopyWith<$Res> - implements $NullablePlayerSettingsCopyWith<$Res> { - factory _$$NullablePlayerSettingsImplCopyWith( - _$NullablePlayerSettingsImpl value, - $Res Function(_$NullablePlayerSettingsImpl) then) = - __$$NullablePlayerSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {MinimizedPlayerSettings? miniPlayerSettings, - ExpandedPlayerSettings? expandedPlayerSettings, - double? preferredDefaultVolume, - double? preferredDefaultSpeed, - List? speedOptions, - SleepTimerSettings? sleepTimerSettings, - Duration? playbackReportInterval}); - @override - $MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings; - @override - $ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings; - @override - $SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings; +/// Adds pattern-matching-related methods to [NullablePlayerSettings]. +extension NullablePlayerSettingsPatterns on NullablePlayerSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _NullablePlayerSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _NullablePlayerSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _NullablePlayerSettings value) $default,){ +final _that = this; +switch (_that) { +case _NullablePlayerSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _NullablePlayerSettings value)? $default,){ +final _that = this; +switch (_that) { +case _NullablePlayerSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List? speedOptions, SleepTimerSettings? sleepTimerSettings, Duration? playbackReportInterval)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _NullablePlayerSettings() when $default != null: +return $default(_that.miniPlayerSettings,_that.expandedPlayerSettings,_that.preferredDefaultVolume,_that.preferredDefaultSpeed,_that.speedOptions,_that.sleepTimerSettings,_that.playbackReportInterval);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List? speedOptions, SleepTimerSettings? sleepTimerSettings, Duration? playbackReportInterval) $default,) {final _that = this; +switch (_that) { +case _NullablePlayerSettings(): +return $default(_that.miniPlayerSettings,_that.expandedPlayerSettings,_that.preferredDefaultVolume,_that.preferredDefaultSpeed,_that.speedOptions,_that.sleepTimerSettings,_that.playbackReportInterval);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List? speedOptions, SleepTimerSettings? sleepTimerSettings, Duration? playbackReportInterval)? $default,) {final _that = this; +switch (_that) { +case _NullablePlayerSettings() when $default != null: +return $default(_that.miniPlayerSettings,_that.expandedPlayerSettings,_that.preferredDefaultVolume,_that.preferredDefaultSpeed,_that.speedOptions,_that.sleepTimerSettings,_that.playbackReportInterval);case _: + return null; + +} } -/// @nodoc -class __$$NullablePlayerSettingsImplCopyWithImpl<$Res> - extends _$NullablePlayerSettingsCopyWithImpl<$Res, - _$NullablePlayerSettingsImpl> - implements _$$NullablePlayerSettingsImplCopyWith<$Res> { - __$$NullablePlayerSettingsImplCopyWithImpl( - _$NullablePlayerSettingsImpl _value, - $Res Function(_$NullablePlayerSettingsImpl) _then) - : super(_value, _then); - - /// Create a copy of NullablePlayerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? miniPlayerSettings = freezed, - Object? expandedPlayerSettings = freezed, - Object? preferredDefaultVolume = freezed, - Object? preferredDefaultSpeed = freezed, - Object? speedOptions = freezed, - Object? sleepTimerSettings = freezed, - Object? playbackReportInterval = freezed, - }) { - return _then(_$NullablePlayerSettingsImpl( - miniPlayerSettings: freezed == miniPlayerSettings - ? _value.miniPlayerSettings - : miniPlayerSettings // ignore: cast_nullable_to_non_nullable - as MinimizedPlayerSettings?, - expandedPlayerSettings: freezed == expandedPlayerSettings - ? _value.expandedPlayerSettings - : expandedPlayerSettings // ignore: cast_nullable_to_non_nullable - as ExpandedPlayerSettings?, - preferredDefaultVolume: freezed == preferredDefaultVolume - ? _value.preferredDefaultVolume - : preferredDefaultVolume // ignore: cast_nullable_to_non_nullable - as double?, - preferredDefaultSpeed: freezed == preferredDefaultSpeed - ? _value.preferredDefaultSpeed - : preferredDefaultSpeed // ignore: cast_nullable_to_non_nullable - as double?, - speedOptions: freezed == speedOptions - ? _value._speedOptions - : speedOptions // ignore: cast_nullable_to_non_nullable - as List?, - sleepTimerSettings: freezed == sleepTimerSettings - ? _value.sleepTimerSettings - : sleepTimerSettings // ignore: cast_nullable_to_non_nullable - as SleepTimerSettings?, - playbackReportInterval: freezed == playbackReportInterval - ? _value.playbackReportInterval - : playbackReportInterval // ignore: cast_nullable_to_non_nullable - as Duration?, - )); - } } /// @nodoc @JsonSerializable() -class _$NullablePlayerSettingsImpl implements _NullablePlayerSettings { - const _$NullablePlayerSettingsImpl( - {this.miniPlayerSettings, - this.expandedPlayerSettings, - this.preferredDefaultVolume, - this.preferredDefaultSpeed, - final List? speedOptions, - this.sleepTimerSettings, - this.playbackReportInterval}) - : _speedOptions = speedOptions; - factory _$NullablePlayerSettingsImpl.fromJson(Map json) => - _$$NullablePlayerSettingsImplFromJson(json); +class _NullablePlayerSettings implements NullablePlayerSettings { + const _NullablePlayerSettings({this.miniPlayerSettings, this.expandedPlayerSettings, this.preferredDefaultVolume, this.preferredDefaultSpeed, final List? speedOptions, this.sleepTimerSettings, this.playbackReportInterval}): _speedOptions = speedOptions; + factory _NullablePlayerSettings.fromJson(Map json) => _$NullablePlayerSettingsFromJson(json); - @override - final MinimizedPlayerSettings? miniPlayerSettings; - @override - final ExpandedPlayerSettings? expandedPlayerSettings; - @override - final double? preferredDefaultVolume; - @override - final double? preferredDefaultSpeed; - final List? _speedOptions; - @override - List? get speedOptions { - final value = _speedOptions; - if (value == null) return null; - if (_speedOptions is EqualUnmodifiableListView) return _speedOptions; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(value); - } - - @override - final SleepTimerSettings? sleepTimerSettings; - @override - final Duration? playbackReportInterval; - - @override - String toString() { - return 'NullablePlayerSettings(miniPlayerSettings: $miniPlayerSettings, expandedPlayerSettings: $expandedPlayerSettings, preferredDefaultVolume: $preferredDefaultVolume, preferredDefaultSpeed: $preferredDefaultSpeed, speedOptions: $speedOptions, sleepTimerSettings: $sleepTimerSettings, playbackReportInterval: $playbackReportInterval)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$NullablePlayerSettingsImpl && - (identical(other.miniPlayerSettings, miniPlayerSettings) || - other.miniPlayerSettings == miniPlayerSettings) && - (identical(other.expandedPlayerSettings, expandedPlayerSettings) || - other.expandedPlayerSettings == expandedPlayerSettings) && - (identical(other.preferredDefaultVolume, preferredDefaultVolume) || - other.preferredDefaultVolume == preferredDefaultVolume) && - (identical(other.preferredDefaultSpeed, preferredDefaultSpeed) || - other.preferredDefaultSpeed == preferredDefaultSpeed) && - const DeepCollectionEquality() - .equals(other._speedOptions, _speedOptions) && - (identical(other.sleepTimerSettings, sleepTimerSettings) || - other.sleepTimerSettings == sleepTimerSettings) && - (identical(other.playbackReportInterval, playbackReportInterval) || - other.playbackReportInterval == playbackReportInterval)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - miniPlayerSettings, - expandedPlayerSettings, - preferredDefaultVolume, - preferredDefaultSpeed, - const DeepCollectionEquality().hash(_speedOptions), - sleepTimerSettings, - playbackReportInterval); - - /// Create a copy of NullablePlayerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$NullablePlayerSettingsImplCopyWith<_$NullablePlayerSettingsImpl> - get copyWith => __$$NullablePlayerSettingsImplCopyWithImpl< - _$NullablePlayerSettingsImpl>(this, _$identity); - - @override - Map toJson() { - return _$$NullablePlayerSettingsImplToJson( - this, - ); - } +@override final MinimizedPlayerSettings? miniPlayerSettings; +@override final ExpandedPlayerSettings? expandedPlayerSettings; +@override final double? preferredDefaultVolume; +@override final double? preferredDefaultSpeed; + final List? _speedOptions; +@override List? get speedOptions { + final value = _speedOptions; + if (value == null) return null; + if (_speedOptions is EqualUnmodifiableListView) return _speedOptions; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); } -abstract class _NullablePlayerSettings implements NullablePlayerSettings { - const factory _NullablePlayerSettings( - {final MinimizedPlayerSettings? miniPlayerSettings, - final ExpandedPlayerSettings? expandedPlayerSettings, - final double? preferredDefaultVolume, - final double? preferredDefaultSpeed, - final List? speedOptions, - final SleepTimerSettings? sleepTimerSettings, - final Duration? playbackReportInterval}) = _$NullablePlayerSettingsImpl; +@override final SleepTimerSettings? sleepTimerSettings; +@override final Duration? playbackReportInterval; - factory _NullablePlayerSettings.fromJson(Map json) = - _$NullablePlayerSettingsImpl.fromJson; +/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$NullablePlayerSettingsCopyWith<_NullablePlayerSettings> get copyWith => __$NullablePlayerSettingsCopyWithImpl<_NullablePlayerSettings>(this, _$identity); - @override - MinimizedPlayerSettings? get miniPlayerSettings; - @override - ExpandedPlayerSettings? get expandedPlayerSettings; - @override - double? get preferredDefaultVolume; - @override - double? get preferredDefaultSpeed; - @override - List? get speedOptions; - @override - SleepTimerSettings? get sleepTimerSettings; - @override - Duration? get playbackReportInterval; - - /// Create a copy of NullablePlayerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$NullablePlayerSettingsImplCopyWith<_$NullablePlayerSettingsImpl> - get copyWith => throw _privateConstructorUsedError; +@override +Map toJson() { + return _$NullablePlayerSettingsToJson(this, ); } + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _NullablePlayerSettings&&(identical(other.miniPlayerSettings, miniPlayerSettings) || other.miniPlayerSettings == miniPlayerSettings)&&(identical(other.expandedPlayerSettings, expandedPlayerSettings) || other.expandedPlayerSettings == expandedPlayerSettings)&&(identical(other.preferredDefaultVolume, preferredDefaultVolume) || other.preferredDefaultVolume == preferredDefaultVolume)&&(identical(other.preferredDefaultSpeed, preferredDefaultSpeed) || other.preferredDefaultSpeed == preferredDefaultSpeed)&&const DeepCollectionEquality().equals(other._speedOptions, _speedOptions)&&(identical(other.sleepTimerSettings, sleepTimerSettings) || other.sleepTimerSettings == sleepTimerSettings)&&(identical(other.playbackReportInterval, playbackReportInterval) || other.playbackReportInterval == playbackReportInterval)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,miniPlayerSettings,expandedPlayerSettings,preferredDefaultVolume,preferredDefaultSpeed,const DeepCollectionEquality().hash(_speedOptions),sleepTimerSettings,playbackReportInterval); + +@override +String toString() { + return 'NullablePlayerSettings(miniPlayerSettings: $miniPlayerSettings, expandedPlayerSettings: $expandedPlayerSettings, preferredDefaultVolume: $preferredDefaultVolume, preferredDefaultSpeed: $preferredDefaultSpeed, speedOptions: $speedOptions, sleepTimerSettings: $sleepTimerSettings, playbackReportInterval: $playbackReportInterval)'; +} + + +} + +/// @nodoc +abstract mixin class _$NullablePlayerSettingsCopyWith<$Res> implements $NullablePlayerSettingsCopyWith<$Res> { + factory _$NullablePlayerSettingsCopyWith(_NullablePlayerSettings value, $Res Function(_NullablePlayerSettings) _then) = __$NullablePlayerSettingsCopyWithImpl; +@override @useResult +$Res call({ + MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List? speedOptions, SleepTimerSettings? sleepTimerSettings, Duration? playbackReportInterval +}); + + +@override $MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings;@override $ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings;@override $SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings; + +} +/// @nodoc +class __$NullablePlayerSettingsCopyWithImpl<$Res> + implements _$NullablePlayerSettingsCopyWith<$Res> { + __$NullablePlayerSettingsCopyWithImpl(this._self, this._then); + + final _NullablePlayerSettings _self; + final $Res Function(_NullablePlayerSettings) _then; + +/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? miniPlayerSettings = freezed,Object? expandedPlayerSettings = freezed,Object? preferredDefaultVolume = freezed,Object? preferredDefaultSpeed = freezed,Object? speedOptions = freezed,Object? sleepTimerSettings = freezed,Object? playbackReportInterval = freezed,}) { + return _then(_NullablePlayerSettings( +miniPlayerSettings: freezed == miniPlayerSettings ? _self.miniPlayerSettings : miniPlayerSettings // ignore: cast_nullable_to_non_nullable +as MinimizedPlayerSettings?,expandedPlayerSettings: freezed == expandedPlayerSettings ? _self.expandedPlayerSettings : expandedPlayerSettings // ignore: cast_nullable_to_non_nullable +as ExpandedPlayerSettings?,preferredDefaultVolume: freezed == preferredDefaultVolume ? _self.preferredDefaultVolume : preferredDefaultVolume // ignore: cast_nullable_to_non_nullable +as double?,preferredDefaultSpeed: freezed == preferredDefaultSpeed ? _self.preferredDefaultSpeed : preferredDefaultSpeed // ignore: cast_nullable_to_non_nullable +as double?,speedOptions: freezed == speedOptions ? _self._speedOptions : speedOptions // ignore: cast_nullable_to_non_nullable +as List?,sleepTimerSettings: freezed == sleepTimerSettings ? _self.sleepTimerSettings : sleepTimerSettings // ignore: cast_nullable_to_non_nullable +as SleepTimerSettings?,playbackReportInterval: freezed == playbackReportInterval ? _self.playbackReportInterval : playbackReportInterval // ignore: cast_nullable_to_non_nullable +as Duration?, + )); +} + +/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings { + if (_self.miniPlayerSettings == null) { + return null; + } + + return $MinimizedPlayerSettingsCopyWith<$Res>(_self.miniPlayerSettings!, (value) { + return _then(_self.copyWith(miniPlayerSettings: value)); + }); +}/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings { + if (_self.expandedPlayerSettings == null) { + return null; + } + + return $ExpandedPlayerSettingsCopyWith<$Res>(_self.expandedPlayerSettings!, (value) { + return _then(_self.copyWith(expandedPlayerSettings: value)); + }); +}/// Create a copy of NullablePlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings { + if (_self.sleepTimerSettings == null) { + return null; + } + + return $SleepTimerSettingsCopyWith<$Res>(_self.sleepTimerSettings!, (value) { + return _then(_self.copyWith(sleepTimerSettings: value)); + }); +} +} + +// dart format on diff --git a/lib/features/per_book_settings/models/nullable_player_settings.g.dart b/lib/features/per_book_settings/models/nullable_player_settings.g.dart index 28eb3bc..3c45864 100644 --- a/lib/features/per_book_settings/models/nullable_player_settings.g.dart +++ b/lib/features/per_book_settings/models/nullable_player_settings.g.dart @@ -6,42 +6,42 @@ part of 'nullable_player_settings.dart'; // JsonSerializableGenerator // ************************************************************************** -_$NullablePlayerSettingsImpl _$$NullablePlayerSettingsImplFromJson( - Map json) => - _$NullablePlayerSettingsImpl( - miniPlayerSettings: json['miniPlayerSettings'] == null - ? null - : MinimizedPlayerSettings.fromJson( - json['miniPlayerSettings'] as Map), - expandedPlayerSettings: json['expandedPlayerSettings'] == null - ? null - : ExpandedPlayerSettings.fromJson( - json['expandedPlayerSettings'] as Map), - preferredDefaultVolume: - (json['preferredDefaultVolume'] as num?)?.toDouble(), - preferredDefaultSpeed: - (json['preferredDefaultSpeed'] as num?)?.toDouble(), - speedOptions: (json['speedOptions'] as List?) - ?.map((e) => (e as num).toDouble()) - .toList(), - sleepTimerSettings: json['sleepTimerSettings'] == null - ? null - : SleepTimerSettings.fromJson( - json['sleepTimerSettings'] as Map), - playbackReportInterval: json['playbackReportInterval'] == null - ? null - : Duration( - microseconds: (json['playbackReportInterval'] as num).toInt()), - ); +_NullablePlayerSettings _$NullablePlayerSettingsFromJson( + Map json, +) => _NullablePlayerSettings( + miniPlayerSettings: json['miniPlayerSettings'] == null + ? null + : MinimizedPlayerSettings.fromJson( + json['miniPlayerSettings'] as Map, + ), + expandedPlayerSettings: json['expandedPlayerSettings'] == null + ? null + : ExpandedPlayerSettings.fromJson( + json['expandedPlayerSettings'] as Map, + ), + preferredDefaultVolume: (json['preferredDefaultVolume'] as num?)?.toDouble(), + preferredDefaultSpeed: (json['preferredDefaultSpeed'] as num?)?.toDouble(), + speedOptions: (json['speedOptions'] as List?) + ?.map((e) => (e as num).toDouble()) + .toList(), + sleepTimerSettings: json['sleepTimerSettings'] == null + ? null + : SleepTimerSettings.fromJson( + json['sleepTimerSettings'] as Map, + ), + playbackReportInterval: json['playbackReportInterval'] == null + ? null + : Duration(microseconds: (json['playbackReportInterval'] as num).toInt()), +); -Map _$$NullablePlayerSettingsImplToJson( - _$NullablePlayerSettingsImpl instance) => - { - 'miniPlayerSettings': instance.miniPlayerSettings, - 'expandedPlayerSettings': instance.expandedPlayerSettings, - 'preferredDefaultVolume': instance.preferredDefaultVolume, - 'preferredDefaultSpeed': instance.preferredDefaultSpeed, - 'speedOptions': instance.speedOptions, - 'sleepTimerSettings': instance.sleepTimerSettings, - 'playbackReportInterval': instance.playbackReportInterval?.inMicroseconds, - }; +Map _$NullablePlayerSettingsToJson( + _NullablePlayerSettings instance, +) => { + 'miniPlayerSettings': instance.miniPlayerSettings, + 'expandedPlayerSettings': instance.expandedPlayerSettings, + 'preferredDefaultVolume': instance.preferredDefaultVolume, + 'preferredDefaultSpeed': instance.preferredDefaultSpeed, + 'speedOptions': instance.speedOptions, + 'sleepTimerSettings': instance.sleepTimerSettings, + 'playbackReportInterval': instance.playbackReportInterval?.inMicroseconds, +}; diff --git a/lib/features/per_book_settings/providers/book_settings_provider.g.dart b/lib/features/per_book_settings/providers/book_settings_provider.g.dart index 943bd55..b8ed222 100644 --- a/lib/features/per_book_settings/providers/book_settings_provider.g.dart +++ b/lib/features/per_book_settings/providers/book_settings_provider.g.dart @@ -6,169 +6,102 @@ part of 'book_settings_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$bookSettingsHash() => r'b976df954edf98ec6ccb3eb41e9d07dd4a9193eb'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -abstract class _$BookSettings - extends BuildlessAutoDisposeNotifier { - late final String bookId; - - model.BookSettings build( - String bookId, - ); -} - -/// See also [BookSettings]. @ProviderFor(BookSettings) -const bookSettingsProvider = BookSettingsFamily(); +final bookSettingsProvider = BookSettingsFamily._(); -/// See also [BookSettings]. -class BookSettingsFamily extends Family { - /// See also [BookSettings]. - const BookSettingsFamily(); +final class BookSettingsProvider + extends $NotifierProvider { + BookSettingsProvider._({ + required BookSettingsFamily super.from, + required String super.argument, + }) : super( + retry: null, + name: r'bookSettingsProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); - /// See also [BookSettings]. - BookSettingsProvider call( - String bookId, - ) { - return BookSettingsProvider( - bookId, - ); + @override + String debugGetCreateSourceHash() => _$bookSettingsHash(); + + @override + String toString() { + return r'bookSettingsProvider' + '' + '($argument)'; } + @$internal @override - BookSettingsProvider getProviderOverride( - covariant BookSettingsProvider provider, - ) { - return call( - provider.bookId, - ); - } + BookSettings create() => BookSettings(); - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'bookSettingsProvider'; -} - -/// See also [BookSettings]. -class BookSettingsProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [BookSettings]. - BookSettingsProvider( - String bookId, - ) : this._internal( - () => BookSettings()..bookId = bookId, - from: bookSettingsProvider, - name: r'bookSettingsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$bookSettingsHash, - dependencies: BookSettingsFamily._dependencies, - allTransitiveDependencies: - BookSettingsFamily._allTransitiveDependencies, - bookId: bookId, - ); - - BookSettingsProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.bookId, - }) : super.internal(); - - final String bookId; - - @override - model.BookSettings runNotifierBuild( - covariant BookSettings notifier, - ) { - return notifier.build( - bookId, - ); - } - - @override - Override overrideWith(BookSettings Function() create) { - return ProviderOverride( + /// {@macro riverpod.override_with_value} + Override overrideWithValue(model.BookSettings value) { + return $ProviderOverride( origin: this, - override: BookSettingsProvider._internal( - () => create()..bookId = bookId, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - bookId: bookId, - ), + providerOverride: $SyncValueProvider(value), ); } - @override - AutoDisposeNotifierProviderElement - createElement() { - return _BookSettingsProviderElement(this); - } - @override bool operator ==(Object other) { - return other is BookSettingsProvider && other.bookId == bookId; + return other is BookSettingsProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, bookId.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -mixin BookSettingsRef on AutoDisposeNotifierProviderRef { - /// The parameter `bookId` of this provider. - String get bookId; -} +String _$bookSettingsHash() => r'b976df954edf98ec6ccb3eb41e9d07dd4a9193eb'; -class _BookSettingsProviderElement - extends AutoDisposeNotifierProviderElement - with BookSettingsRef { - _BookSettingsProviderElement(super.provider); +final class BookSettingsFamily extends $Family + with + $ClassFamilyOverride< + BookSettings, + model.BookSettings, + model.BookSettings, + model.BookSettings, + String + > { + BookSettingsFamily._() + : super( + retry: null, + name: r'bookSettingsProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + BookSettingsProvider call(String bookId) => + BookSettingsProvider._(argument: bookId, from: this); @override - String get bookId => (origin as BookSettingsProvider).bookId; + String toString() => r'bookSettingsProvider'; +} + +abstract class _$BookSettings extends $Notifier { + late final _$args = ref.$arg as String; + String get bookId => _$args; + + model.BookSettings build(String bookId); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + model.BookSettings, + Object?, + Object? + >; + element.handleCreate(ref, () => build(_$args)); + } } -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/features/playback_reporting/core/playback_reporter.dart b/lib/features/playback_reporting/core/playback_reporter.dart index faffb4d..e140bf6 100644 --- a/lib/features/playback_reporting/core/playback_reporter.dart +++ b/lib/features/playback_reporting/core/playback_reporter.dart @@ -1,8 +1,10 @@ import 'dart:async'; +import 'package:http/http.dart' as http; import 'package:logging/logging.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:vaani/features/player/core/audiobook_player.dart'; +import 'package:vaani/shared/extensions/obfuscation.dart'; final _logger = Logger('PlaybackReporter'); @@ -124,9 +126,7 @@ class PlaybackReporter { } Future tryReportPlayback(_) async { - _logger.fine( - 'callback called when elapsed ${_stopwatch.elapsed}', - ); + _logger.fine('callback called when elapsed ${_stopwatch.elapsed}'); if (player.book != null && player.positionInBook >= player.book!.duration - markCompleteWhenTimeLeft) { @@ -255,9 +255,9 @@ class PlaybackReporter { _logger.fine('cancelled timer'); } - void _responseErrorHandler(response, [error]) { + void _responseErrorHandler(http.Response response, [error]) { if (response.statusCode != 200) { - _logger.shout('Error with api: $response, $error'); + _logger.severe('Error with api: ${response.obfuscate()}, $error'); throw PlaybackSyncError( 'Error syncing position: ${response.body}, $error', ); diff --git a/lib/features/playback_reporting/providers/playback_reporter_provider.dart b/lib/features/playback_reporting/providers/playback_reporter_provider.dart index a7ca1d5..1bee521 100644 --- a/lib/features/playback_reporting/providers/playback_reporter_provider.dart +++ b/lib/features/playback_reporting/providers/playback_reporter_provider.dart @@ -20,8 +20,9 @@ class PlaybackReporter extends _$PlaybackReporter { final deviceName = await ref.watch(deviceNameProvider.future); final deviceModel = await ref.watch(deviceModelProvider.future); final deviceSdkVersion = await ref.watch(deviceSdkVersionProvider.future); - final deviceManufacturer = - await ref.watch(deviceManufacturerProvider.future); + final deviceManufacturer = await ref.watch( + deviceManufacturerProvider.future, + ); final reporter = core.PlaybackReporter( player, diff --git a/lib/features/playback_reporting/providers/playback_reporter_provider.g.dart b/lib/features/playback_reporting/providers/playback_reporter_provider.g.dart index 8b8936d..e017dbb 100644 --- a/lib/features/playback_reporting/providers/playback_reporter_provider.g.dart +++ b/lib/features/playback_reporting/providers/playback_reporter_provider.g.dart @@ -6,21 +6,55 @@ part of 'playback_reporter_provider.dart'; // RiverpodGenerator // ************************************************************************** +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(PlaybackReporter) +final playbackReporterProvider = PlaybackReporterProvider._(); + +final class PlaybackReporterProvider + extends $AsyncNotifierProvider { + PlaybackReporterProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'playbackReporterProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$playbackReporterHash(); + + @$internal + @override + PlaybackReporter create() => PlaybackReporter(); +} + String _$playbackReporterHash() => r'f5436d652e51c37bcc684acdaec94e17a97e68e5'; -/// See also [PlaybackReporter]. -@ProviderFor(PlaybackReporter) -final playbackReporterProvider = - AsyncNotifierProvider.internal( - PlaybackReporter.new, - name: r'playbackReporterProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$playbackReporterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PlaybackReporter = AsyncNotifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +abstract class _$PlaybackReporter + extends $AsyncNotifier { + FutureOr build(); + @$mustCallSuper + @override + void runBuild() { + final ref = + this.ref + as $Ref, core.PlaybackReporter>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier< + AsyncValue, + core.PlaybackReporter + >, + AsyncValue, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/features/player/core/audiobook_player.dart b/lib/features/player/core/audiobook_player.dart index 726e91d..c3d7b46 100644 --- a/lib/features/player/core/audiobook_player.dart +++ b/lib/features/player/core/audiobook_player.dart @@ -11,38 +11,37 @@ import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:vaani/settings/app_settings_provider.dart'; import 'package:vaani/settings/models/app_settings.dart'; import 'package:vaani/shared/extensions/model_conversions.dart'; +import 'package:vaani/shared/extensions/obfuscation.dart'; final _logger = Logger('AudiobookPlayer'); /// returns the sum of the duration of all the previous tracks before the [index] Duration sumOfTracks(BookExpanded book, int? index) { + _logger.fine('Calculating sum of tracks for index: $index'); // return 0 if index is less than 0 if (index == null || index < 0) { + _logger.warning('Index is null or less than 0, returning 0'); return Duration.zero; } - return book.tracks.sublist(0, index).fold(Duration.zero, - (previousValue, element) { - return previousValue + element.duration; - }); + final total = book.tracks + .sublist(0, index) + .fold( + Duration.zero, + (previousValue, element) => previousValue + element.duration, + ); + _logger.fine('Sum of tracks for index: $index is $total'); + return total; } /// returns the [AudioTrack] to play based on the [position] in the [book] AudioTrack getTrackToPlay(BookExpanded book, Duration position) { - // var totalDuration = Duration.zero; - // for (var track in book.tracks) { - // totalDuration += track.duration; - // if (totalDuration >= position) { - // return track; - // } - // } - // return book.tracks.last; - return book.tracks.firstWhere( - (element) { - return element.startOffset <= position && - (element.startOffset + element.duration) >= position; - }, - orElse: () => book.tracks.last, - ); + _logger.fine('Getting track to play for position: $position'); + final track = book.tracks.firstWhere((element) { + return element.startOffset <= position && + (element.startOffset + element.duration) >= position; + }, orElse: () => book.tracks.last); + _logger.fine('Track to play for position: $position is $track'); + return track; } /// will manage the audio player instance @@ -50,6 +49,7 @@ class AudiobookPlayer extends AudioPlayer { // constructor which takes in the BookExpanded object AudiobookPlayer(this.token, this.baseUrl) : super() { // set the source of the player to the first track in the book + _logger.config('Setting up audiobook player'); } /// the [BookExpanded] being played @@ -84,20 +84,23 @@ class AudiobookPlayer extends AudioPlayer { List? downloadedUris, Uri? artworkUri, }) async { + _logger.finer( + 'Initial position: $initialPosition, Downloaded URIs: $downloadedUris', + ); final appSettings = loadOrCreateAppSettings(); - // if the book is null, stop the player if (book == null) { _book = null; _logger.info('Book is null, stopping player'); return stop(); } - // see if the book is the same as the current book if (_book == book) { _logger.info('Book is the same, doing nothing'); return; } - // first stop the player and clear the source + _logger.info('Setting source for book: $book'); + + _logger.fine('Stopping player'); await stop(); _book = book; @@ -114,6 +117,7 @@ class AudiobookPlayer extends AudioPlayer { ? initialPosition - trackToPlay.startOffset : null; + _logger.finer('Setting audioSource'); await setAudioSource( preload: preload, initialIndex: initialIndex, @@ -121,10 +125,14 @@ class AudiobookPlayer extends AudioPlayer { ConcatenatingAudioSource( useLazyPreparation: true, children: book.tracks.map((track) { - final retrievedUri = - _getUri(track, downloadedUris, baseUrl: baseUrl, token: token); + final retrievedUri = _getUri( + track, + downloadedUris, + baseUrl: baseUrl, + token: token, + ); _logger.fine( - 'Setting source for track: ${track.title}, URI: $retrievedUri', + 'Setting source for track: ${track.title}, URI: ${retrievedUri.obfuscate()}', ); return AudioSource.uri( retrievedUri, @@ -136,7 +144,8 @@ class AudiobookPlayer extends AudioPlayer { .formatNotificationTitle(book), album: appSettings.notificationSettings.secondaryTitle .formatNotificationTitle(book), - artUri: artworkUri ?? + artUri: + artworkUri ?? Uri.parse( '$baseUrl/api/items/${book.libraryItemId}/cover?token=$token&width=800', ), @@ -145,7 +154,7 @@ class AudiobookPlayer extends AudioPlayer { }).toList(), ), ).catchError((error) { - _logger.shout('Error: $error'); + _logger.shout('Error in setting audio source: $error'); }); } @@ -153,7 +162,7 @@ class AudiobookPlayer extends AudioPlayer { Future togglePlayPause() { // check if book is set if (_book == null) { - throw StateError('No book is set'); + _logger.warning('No book is set, not toggling play/pause'); } // TODO refactor this to cover all the states @@ -169,9 +178,11 @@ class AudiobookPlayer extends AudioPlayer { @override Future seek(Duration? positionInBook, {int? index}) async { if (_book == null) { + _logger.warning('No book is set, not seeking'); return; } if (positionInBook == null) { + _logger.warning('Position given is null, not seeking'); return; } final tracks = _book!.tracks; @@ -248,12 +259,9 @@ class AudiobookPlayer extends AudioPlayer { if (_book!.chapters.isEmpty) { return null; } - return _book!.chapters.firstWhere( - (element) { - return element.start <= positionInBook && element.end >= positionInBook; - }, - orElse: () => _book!.chapters.first, - ); + return _book!.chapters.firstWhere((element) { + return element.start <= positionInBook && element.end >= positionInBook; + }, orElse: () => _book!.chapters.first); } } @@ -264,11 +272,9 @@ Uri _getUri( required String token, }) { // check if the track is in the downloadedUris - final uri = downloadedUris?.firstWhereOrNull( - (element) { - return element.pathSegments.last == track.metadata?.filename; - }, - ); + final uri = downloadedUris?.firstWhereOrNull((element) { + return element.pathSegments.last == track.metadata?.filename; + }); return uri ?? Uri.parse('${baseUrl.toString()}${track.contentUrl}?token=$token'); @@ -276,17 +282,14 @@ Uri _getUri( extension FormatNotificationTitle on String { String formatNotificationTitle(BookExpanded book) { - return replaceAllMapped( - RegExp(r'\$(\w+)'), - (match) { - final type = match.group(1); - return NotificationTitleType.values - .firstWhere((element) => element.name == type) - .extractFrom(book) ?? - match.group(0) ?? - ''; - }, - ); + return replaceAllMapped(RegExp(r'\$(\w+)'), (match) { + final type = match.group(1); + return NotificationTitleType.values + .firstWhere((element) => element.name == type) + .extractFrom(book) ?? + match.group(0) ?? + ''; + }); } } diff --git a/lib/features/player/core/init.dart b/lib/features/player/core/init.dart index abcf013..a95f3c7 100644 --- a/lib/features/player/core/init.dart +++ b/lib/features/player/core/init.dart @@ -30,23 +30,28 @@ Future configurePlayer() async { androidShowNotificationBadge: false, notificationConfigBuilder: (state) { final controls = [ - if (appSettings.notificationSettings.mediaControls - .contains(NotificationMediaControl.skipToPreviousChapter) && + if (appSettings.notificationSettings.mediaControls.contains( + NotificationMediaControl.skipToPreviousChapter, + ) && state.hasPrevious) MediaControl.skipToPrevious, - if (appSettings.notificationSettings.mediaControls - .contains(NotificationMediaControl.rewind)) + if (appSettings.notificationSettings.mediaControls.contains( + NotificationMediaControl.rewind, + )) MediaControl.rewind, if (state.playing) MediaControl.pause else MediaControl.play, - if (appSettings.notificationSettings.mediaControls - .contains(NotificationMediaControl.fastForward)) + if (appSettings.notificationSettings.mediaControls.contains( + NotificationMediaControl.fastForward, + )) MediaControl.fastForward, - if (appSettings.notificationSettings.mediaControls - .contains(NotificationMediaControl.skipToNextChapter) && + if (appSettings.notificationSettings.mediaControls.contains( + NotificationMediaControl.skipToNextChapter, + ) && state.hasNext) MediaControl.skipToNext, - if (appSettings.notificationSettings.mediaControls - .contains(NotificationMediaControl.stop)) + if (appSettings.notificationSettings.mediaControls.contains( + NotificationMediaControl.stop, + )) MediaControl.stop, ]; return NotificationConfig( diff --git a/lib/features/player/playlist.dart b/lib/features/player/playlist.dart index 61c2326..c8baedd 100644 --- a/lib/features/player/playlist.dart +++ b/lib/features/player/playlist.dart @@ -62,8 +62,8 @@ class AudiobookPlaylist { this.books = const [], currentIndex = 0, subCurrentIndex = 0, - }) : _currentIndex = currentIndex, - _subCurrentIndex = subCurrentIndex; + }) : _currentIndex = currentIndex, + _subCurrentIndex = subCurrentIndex; // most important method, gets the audio file to play // this is needed as a library item is a list of audio files diff --git a/lib/features/player/playlist_provider.g.dart b/lib/features/player/playlist_provider.g.dart index 061289c..2baca58 100644 --- a/lib/features/player/playlist_provider.g.dart +++ b/lib/features/player/playlist_provider.g.dart @@ -6,20 +6,57 @@ part of 'playlist_provider.dart'; // RiverpodGenerator // ************************************************************************** +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(Playlist) +final playlistProvider = PlaylistProvider._(); + +final class PlaylistProvider + extends $NotifierProvider { + PlaylistProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'playlistProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$playlistHash(); + + @$internal + @override + Playlist create() => Playlist(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AudiobookPlaylist value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + String _$playlistHash() => r'bed4642e4c2de829e4d0630cb5bf92bffeeb1f60'; -/// See also [Playlist]. -@ProviderFor(Playlist) -final playlistProvider = - AutoDisposeNotifierProvider.internal( - Playlist.new, - name: r'playlistProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$playlistHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Playlist = AutoDisposeNotifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +abstract class _$Playlist extends $Notifier { + AudiobookPlaylist build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + AudiobookPlaylist, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/features/player/providers/audiobook_player.dart b/lib/features/player/providers/audiobook_player.dart index 947ca57..29fede9 100644 --- a/lib/features/player/providers/audiobook_player.dart +++ b/lib/features/player/providers/audiobook_player.dart @@ -1,21 +1,11 @@ +import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:vaani/api/api_provider.dart'; -import 'package:vaani/features/player/core/audiobook_player.dart' - as core; +import 'package:vaani/features/player/core/audiobook_player.dart' as core; part 'audiobook_player.g.dart'; -// @Riverpod(keepAlive: true) -// core.AudiobookPlayer audiobookPlayer( -// AudiobookPlayerRef ref, -// ) { -// final api = ref.watch(authenticatedApiProvider); -// final player = core.AudiobookPlayer(api.token!, api.baseUrl); - -// ref.onDispose(player.dispose); - -// return player; -// } +final _logger = Logger('AudiobookPlayerProvider'); const playerId = 'audiobook_player'; @@ -26,12 +16,10 @@ class SimpleAudiobookPlayer extends _$SimpleAudiobookPlayer { @override core.AudiobookPlayer build() { final api = ref.watch(authenticatedApiProvider); - final player = core.AudiobookPlayer( - api.token!, - api.baseUrl, - ); + final player = core.AudiobookPlayer(api.token!, api.baseUrl); ref.onDispose(player.dispose); + _logger.finer('created simple player'); return player; } @@ -47,18 +35,16 @@ class AudiobookPlayer extends _$AudiobookPlayer { // bind notify listeners to the player player.playerStateStream.listen((_) { - notifyListeners(); + ref.notifyListeners(); }); + _logger.finer('created player'); + return player; } - void notifyListeners() { - ref.notifyListeners(); - } - Future setSpeed(double speed) async { await state.setSpeed(speed); - notifyListeners(); + ref.notifyListeners(); } } diff --git a/lib/features/player/providers/audiobook_player.g.dart b/lib/features/player/providers/audiobook_player.g.dart index 849cde6..900fd73 100644 --- a/lib/features/player/providers/audiobook_player.g.dart +++ b/lib/features/player/providers/audiobook_player.g.dart @@ -6,41 +6,119 @@ part of 'audiobook_player.dart'; // RiverpodGenerator // ************************************************************************** -String _$simpleAudiobookPlayerHash() => - r'9e11ed2791d35e308f8cbe61a79a45cf51466ebb'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// Simple because it doesn't rebuild when the player state changes +/// it only rebuilds when the token changes + +@ProviderFor(SimpleAudiobookPlayer) +final simpleAudiobookPlayerProvider = SimpleAudiobookPlayerProvider._(); /// Simple because it doesn't rebuild when the player state changes /// it only rebuilds when the token changes -/// -/// Copied from [SimpleAudiobookPlayer]. -@ProviderFor(SimpleAudiobookPlayer) -final simpleAudiobookPlayerProvider = - NotifierProvider.internal( - SimpleAudiobookPlayer.new, - name: r'simpleAudiobookPlayerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$simpleAudiobookPlayerHash, - dependencies: null, - allTransitiveDependencies: null, -); +final class SimpleAudiobookPlayerProvider + extends $NotifierProvider { + /// Simple because it doesn't rebuild when the player state changes + /// it only rebuilds when the token changes + SimpleAudiobookPlayerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'simpleAudiobookPlayerProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); -typedef _$SimpleAudiobookPlayer = Notifier; -String _$audiobookPlayerHash() => r'44394b1dbbf85eb19ef1f693717e8cbc15b768e5'; + @override + String debugGetCreateSourceHash() => _$simpleAudiobookPlayerHash(); + + @$internal + @override + SimpleAudiobookPlayer create() => SimpleAudiobookPlayer(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(core.AudiobookPlayer value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$simpleAudiobookPlayerHash() => + r'5e94bbff4314adceb5affa704fc4d079d4016afa'; + +/// Simple because it doesn't rebuild when the player state changes +/// it only rebuilds when the token changes + +abstract class _$SimpleAudiobookPlayer extends $Notifier { + core.AudiobookPlayer build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + core.AudiobookPlayer, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} -/// See also [AudiobookPlayer]. @ProviderFor(AudiobookPlayer) -final audiobookPlayerProvider = - NotifierProvider.internal( - AudiobookPlayer.new, - name: r'audiobookPlayerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$audiobookPlayerHash, - dependencies: null, - allTransitiveDependencies: null, -); +final audiobookPlayerProvider = AudiobookPlayerProvider._(); -typedef _$AudiobookPlayer = Notifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +final class AudiobookPlayerProvider + extends $NotifierProvider { + AudiobookPlayerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'audiobookPlayerProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$audiobookPlayerHash(); + + @$internal + @override + AudiobookPlayer create() => AudiobookPlayer(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(core.AudiobookPlayer value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$audiobookPlayerHash() => r'0f180308067486896fec6a65a6afb0e6686ac4a0'; + +abstract class _$AudiobookPlayer extends $Notifier { + core.AudiobookPlayer build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + core.AudiobookPlayer, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/features/player/providers/currently_playing_provider.dart b/lib/features/player/providers/currently_playing_provider.dart index 67e0a35..e8b8af9 100644 --- a/lib/features/player/providers/currently_playing_provider.dart +++ b/lib/features/player/providers/currently_playing_provider.dart @@ -1,3 +1,5 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:vaani/features/player/providers/audiobook_player.dart'; @@ -5,19 +7,22 @@ import 'package:vaani/shared/extensions/model_conversions.dart'; part 'currently_playing_provider.g.dart'; +final _logger = Logger('CurrentlyPlayingProvider'); + @riverpod -BookExpanded? currentlyPlayingBook(CurrentlyPlayingBookRef ref) { +BookExpanded? currentlyPlayingBook(Ref ref) { try { final player = ref.watch(audiobookPlayerProvider); return player.book; } catch (e) { + _logger.warning('Error getting currently playing book: $e'); return null; } } /// provided the current chapter of the book being played @riverpod -BookChapter? currentPlayingChapter(CurrentPlayingChapterRef ref) { +BookChapter? currentPlayingChapter(Ref ref) { final player = ref.watch(audiobookPlayerProvider); player.slowPositionStream.listen((_) { ref.invalidateSelf(); @@ -28,7 +33,7 @@ BookChapter? currentPlayingChapter(CurrentPlayingChapterRef ref) { /// provides the book metadata of the currently playing book @riverpod -BookMetadataExpanded? currentBookMetadata(CurrentBookMetadataRef ref) { +BookMetadataExpanded? currentBookMetadata(Ref ref) { final player = ref.watch(audiobookPlayerProvider); if (player.book == null) return null; return player.book!.metadata.asBookMetadataExpanded; diff --git a/lib/features/player/providers/currently_playing_provider.g.dart b/lib/features/player/providers/currently_playing_provider.g.dart index fb22028..f886f8e 100644 --- a/lib/features/player/providers/currently_playing_provider.g.dart +++ b/lib/features/player/providers/currently_playing_provider.g.dart @@ -6,60 +6,147 @@ part of 'currently_playing_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$currentlyPlayingBookHash() => - r'52334c7b4d68fd498a2a00208d8d7f1ba0085237'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning -/// See also [currentlyPlayingBook]. @ProviderFor(currentlyPlayingBook) -final currentlyPlayingBookProvider = - AutoDisposeProvider.internal( - currentlyPlayingBook, - name: r'currentlyPlayingBookProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$currentlyPlayingBookHash, - dependencies: null, - allTransitiveDependencies: null, -); +final currentlyPlayingBookProvider = CurrentlyPlayingBookProvider._(); -typedef CurrentlyPlayingBookRef = AutoDisposeProviderRef; -String _$currentPlayingChapterHash() => - r'a084da724e3d8bb1b1475e867ab3200d7d61d827'; +final class CurrentlyPlayingBookProvider + extends $FunctionalProvider + with $Provider { + CurrentlyPlayingBookProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'currentlyPlayingBookProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$currentlyPlayingBookHash(); + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(pointer); + + @override + BookExpanded? create(Ref ref) { + return currentlyPlayingBook(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(BookExpanded? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$currentlyPlayingBookHash() => + r'e4258694c8f0d1e89651b330fae0f672ca13a484'; /// provided the current chapter of the book being played -/// -/// Copied from [currentPlayingChapter]. -@ProviderFor(currentPlayingChapter) -final currentPlayingChapterProvider = - AutoDisposeProvider.internal( - currentPlayingChapter, - name: r'currentPlayingChapterProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$currentPlayingChapterHash, - dependencies: null, - allTransitiveDependencies: null, -); -typedef CurrentPlayingChapterRef = AutoDisposeProviderRef; -String _$currentBookMetadataHash() => - r'9088debba151894b61f2dcba1bba12a89244b9b1'; +@ProviderFor(currentPlayingChapter) +final currentPlayingChapterProvider = CurrentPlayingChapterProvider._(); + +/// provided the current chapter of the book being played + +final class CurrentPlayingChapterProvider + extends $FunctionalProvider + with $Provider { + /// provided the current chapter of the book being played + CurrentPlayingChapterProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'currentPlayingChapterProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$currentPlayingChapterHash(); + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(pointer); + + @override + BookChapter? create(Ref ref) { + return currentPlayingChapter(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(BookChapter? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$currentPlayingChapterHash() => + r'73db8b8a9058573bb0c68ec5d5f8aba9306f3d24'; /// provides the book metadata of the currently playing book -/// -/// Copied from [currentBookMetadata]. -@ProviderFor(currentBookMetadata) -final currentBookMetadataProvider = - AutoDisposeProvider.internal( - currentBookMetadata, - name: r'currentBookMetadataProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$currentBookMetadataHash, - dependencies: null, - allTransitiveDependencies: null, -); -typedef CurrentBookMetadataRef = AutoDisposeProviderRef; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +@ProviderFor(currentBookMetadata) +final currentBookMetadataProvider = CurrentBookMetadataProvider._(); + +/// provides the book metadata of the currently playing book + +final class CurrentBookMetadataProvider + extends + $FunctionalProvider< + BookMetadataExpanded?, + BookMetadataExpanded?, + BookMetadataExpanded? + > + with $Provider { + /// provides the book metadata of the currently playing book + CurrentBookMetadataProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'currentBookMetadataProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$currentBookMetadataHash(); + + @$internal + @override + $ProviderElement $createElement( + $ProviderPointer pointer, + ) => $ProviderElement(pointer); + + @override + BookMetadataExpanded? create(Ref ref) { + return currentBookMetadata(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(BookMetadataExpanded? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$currentBookMetadataHash() => + r'f537ef4ef19280bc952de658ecf6520c535ae344'; diff --git a/lib/features/player/providers/player_form.dart b/lib/features/player/providers/player_form.dart index 975f47f..9652486 100644 --- a/lib/features/player/providers/player_form.dart +++ b/lib/features/player/providers/player_form.dart @@ -6,6 +6,7 @@ import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:miniplayer/miniplayer.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:vaani/features/player/providers/audiobook_player.dart'; part 'player_form.g.dart'; @@ -25,11 +26,10 @@ extension on Ref { } @Riverpod(keepAlive: true) -Raw> playerExpandProgressNotifier( - PlayerExpandProgressNotifierRef ref, -) { - final ValueNotifier playerExpandProgress = - ValueNotifier(playerMinHeight); +Raw> playerExpandProgressNotifier(Ref ref) { + final ValueNotifier playerExpandProgress = ValueNotifier( + playerMinHeight, + ); return ref.disposeAndListenChangeNotifier(playerExpandProgress); } @@ -45,10 +45,8 @@ Raw> playerExpandProgressNotifier( // a provider that will listen to the playerExpandProgressNotifier and return the percentage of the player expanded @Riverpod(keepAlive: true) -double playerHeight( - PlayerHeightRef ref, -) { - final playerExpandProgress = ref.watch(playerExpandProgressNotifierProvider); +double playerHeight(Ref ref) { + final playerExpandProgress = ref.watch(playerExpandProgressProvider); // on change of the playerExpandProgress invalidate playerExpandProgress.addListener(() { @@ -60,3 +58,18 @@ double playerHeight( } final audioBookMiniplayerController = MiniplayerController(); + +@Riverpod(keepAlive: true) +bool isPlayerActive(Ref ref) { + try { + final player = ref.watch(audiobookPlayerProvider); + if (player.book != null) { + return true; + } else { + final playerHeight = ref.watch(playerHeightProvider); + return playerHeight < playerMinHeight; + } + } catch (e) { + return false; + } +} diff --git a/lib/features/player/providers/player_form.g.dart b/lib/features/player/providers/player_form.g.dart index b3b73d9..b2d17ce 100644 --- a/lib/features/player/providers/player_form.g.dart +++ b/lib/features/player/providers/player_form.g.dart @@ -6,37 +6,134 @@ part of 'player_form.dart'; // RiverpodGenerator // ************************************************************************** -String _$playerExpandProgressNotifierHash() => - r'e4817361b9a311b61ca23e51082ed11b0a1120ab'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning -/// See also [playerExpandProgressNotifier]. @ProviderFor(playerExpandProgressNotifier) -final playerExpandProgressNotifierProvider = - Provider>>.internal( - playerExpandProgressNotifier, - name: r'playerExpandProgressNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$playerExpandProgressNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); +final playerExpandProgressProvider = PlayerExpandProgressNotifierProvider._(); -typedef PlayerExpandProgressNotifierRef - = ProviderRef>>; -String _$playerHeightHash() => r'26dbcb180d494575488d700bd5bdb58c02c224a9'; +final class PlayerExpandProgressNotifierProvider + extends + $FunctionalProvider< + Raw>, + Raw>, + Raw> + > + with $Provider>> { + PlayerExpandProgressNotifierProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'playerExpandProgressProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$playerExpandProgressNotifierHash(); + + @$internal + @override + $ProviderElement>> $createElement( + $ProviderPointer pointer, + ) => $ProviderElement(pointer); + + @override + Raw> create(Ref ref) { + return playerExpandProgressNotifier(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider>>(value), + ); + } +} + +String _$playerExpandProgressNotifierHash() => + r'1ac7172d90a070f96222286edd1a176be197f378'; -/// See also [playerHeight]. @ProviderFor(playerHeight) -final playerHeightProvider = Provider.internal( - playerHeight, - name: r'playerHeightProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$playerHeightHash, - dependencies: null, - allTransitiveDependencies: null, -); +final playerHeightProvider = PlayerHeightProvider._(); -typedef PlayerHeightRef = ProviderRef; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +final class PlayerHeightProvider + extends $FunctionalProvider + with $Provider { + PlayerHeightProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'playerHeightProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$playerHeightHash(); + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(pointer); + + @override + double create(Ref ref) { + return playerHeight(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(double value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$playerHeightHash() => r'41144a733b5ffd1c872a237ed7c9ea5f450dd0d4'; + +@ProviderFor(isPlayerActive) +final isPlayerActiveProvider = IsPlayerActiveProvider._(); + +final class IsPlayerActiveProvider extends $FunctionalProvider + with $Provider { + IsPlayerActiveProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'isPlayerActiveProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$isPlayerActiveHash(); + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(pointer); + + @override + bool create(Ref ref) { + return isPlayerActive(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(bool value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$isPlayerActiveHash() => r'2c7ca125423126fb5f0ef218d37bc8fe0ca9ec98'; diff --git a/lib/features/player/view/audiobook_player.dart b/lib/features/player/view/audiobook_player.dart index 46ed7aa..e80202a 100644 --- a/lib/features/player/view/audiobook_player.dart +++ b/lib/features/player/view/audiobook_player.dart @@ -14,7 +14,7 @@ import 'package:vaani/features/player/providers/player_form.dart'; import 'package:vaani/settings/app_settings_provider.dart'; import 'package:vaani/shared/extensions/inverse_lerp.dart'; import 'package:vaani/shared/widgets/shelves/book_shelf.dart'; -import 'package:vaani/theme/theme_from_cover_provider.dart'; +import 'package:vaani/theme/providers/theme_from_cover_provider.dart'; import 'player_when_expanded.dart'; import 'player_when_minimized.dart'; @@ -31,19 +31,15 @@ class AudiobookPlayer extends HookConsumerWidget { if (currentBook == null) { return const SizedBox.shrink(); } - final itemBeingPlayed = - ref.watch(libraryItemProvider(currentBook.libraryItemId)); + final itemBeingPlayed = ref.watch( + libraryItemProvider(currentBook.libraryItemId), + ); final player = ref.watch(audiobookPlayerProvider); - final imageOfItemBeingPlayed = itemBeingPlayed.valueOrNull != null - ? ref.watch( - coverImageProvider(itemBeingPlayed.valueOrNull!.id), - ) + final imageOfItemBeingPlayed = itemBeingPlayed.value != null + ? ref.watch(coverImageProvider(itemBeingPlayed.value!.id)) : null; - final imgWidget = imageOfItemBeingPlayed?.valueOrNull != null - ? Image.memory( - imageOfItemBeingPlayed!.valueOrNull!, - fit: BoxFit.cover, - ) + final imgWidget = imageOfItemBeingPlayed?.value != null + ? Image.memory(imageOfItemBeingPlayed!.value!, fit: BoxFit.cover) : const BookCoverSkeleton(); final playPauseController = useAnimationController( @@ -63,8 +59,11 @@ class AudiobookPlayer extends HookConsumerWidget { // theme from image final imageTheme = ref.watch( themeOfLibraryItemProvider( - itemBeingPlayed.valueOrNull?.id, + itemBeingPlayed.value?.id, brightness: Theme.of(context).brightness, + highContrast: + appSettings.themeSettings.highContrast || + MediaQuery.of(context).highContrast, ), ); @@ -79,15 +78,16 @@ class AudiobookPlayer extends HookConsumerWidget { final preferredVolume = appSettings.playerSettings.preferredDefaultVolume; return Theme( data: ThemeData( - colorScheme: imageTheme.valueOrNull ?? Theme.of(context).colorScheme, + colorScheme: imageTheme.value ?? Theme.of(context).colorScheme, ), child: Miniplayer( - valueNotifier: ref.watch(playerExpandProgressNotifierProvider), + valueNotifier: ref.watch(playerExpandProgressProvider), onDragDown: (percentage) async { // preferred volume // set volume to 0 when dragging down - await player - .setVolume(preferredVolume * (1 - percentage.clamp(0, .75))); + await player.setVolume( + preferredVolume * (1 - percentage.clamp(0, .75)), + ); }, minHeight: playerMinHeight, // subtract the height of notches and other system UI @@ -107,17 +107,14 @@ class AudiobookPlayer extends HookConsumerWidget { // also at this point the image should be at its max size and in the center of the player final miniplayerPercentageDeclaration = (maxImgSize - playerMinHeight) / - (playerMaxHeight - playerMinHeight); + (playerMaxHeight - playerMinHeight); final bool isFormMiniplayer = percentage < miniplayerPercentageDeclaration; if (!isFormMiniplayer) { // this calculation needs a refactor var percentageExpandedPlayer = percentage - .inverseLerp( - miniplayerPercentageDeclaration, - 1, - ) + .inverseLerp(miniplayerPercentageDeclaration, 1) .clamp(0.0, 1.0); return PlayerWhenExpanded( @@ -162,37 +159,33 @@ class AudiobookPlayerPlayPauseButton extends HookConsumerWidget { return switch (player.processingState) { ProcessingState.loading || ProcessingState.buffering => const Padding( - padding: EdgeInsets.all(8.0), - child: CircularProgressIndicator(), - ), + padding: EdgeInsets.all(8.0), + child: CircularProgressIndicator(), + ), ProcessingState.completed => IconButton( - onPressed: () async { - await player.seek(const Duration(seconds: 0)); - await player.play(); - }, - icon: const Icon( - Icons.replay, - ), - ), + onPressed: () async { + await player.seek(const Duration(seconds: 0)); + await player.play(); + }, + icon: const Icon(Icons.replay), + ), ProcessingState.ready => IconButton( - onPressed: () async { - await player.togglePlayPause(); - }, - iconSize: iconSize, - icon: AnimatedIcon( - icon: AnimatedIcons.play_pause, - progress: playPauseController, - ), + onPressed: () async { + await player.togglePlayPause(); + }, + iconSize: iconSize, + icon: AnimatedIcon( + icon: AnimatedIcons.play_pause, + progress: playPauseController, ), + ), ProcessingState.idle => const SizedBox.shrink(), }; } } class AudiobookChapterProgressBar extends HookConsumerWidget { - const AudiobookChapterProgressBar({ - super.key, - }); + const AudiobookChapterProgressBar({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/lib/features/player/view/mini_player_bottom_padding.dart b/lib/features/player/view/mini_player_bottom_padding.dart new file mode 100644 index 0000000..c403361 --- /dev/null +++ b/lib/features/player/view/mini_player_bottom_padding.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:vaani/features/player/providers/player_form.dart'; + +class MiniPlayerBottomPadding extends HookConsumerWidget { + const MiniPlayerBottomPadding({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + return AnimatedSize( + duration: const Duration(milliseconds: 200), + child: ref.watch(isPlayerActiveProvider) + ? const SizedBox(height: playerMinHeight + 8) + : const SizedBox.shrink(), + ); + } +} diff --git a/lib/features/player/view/player_when_expanded.dart b/lib/features/player/view/player_when_expanded.dart index 5fe0ea7..28e6993 100644 --- a/lib/features/player/view/player_when_expanded.dart +++ b/lib/features/player/view/player_when_expanded.dart @@ -38,10 +38,7 @@ class PlayerWhenExpanded extends HookConsumerWidget { const lateStart = 0.4; const earlyEnd = 1; final earlyPercentage = percentageExpandedPlayer - .inverseLerp( - lateStart, - earlyEnd, - ) + .inverseLerp(lateStart, earlyEnd) .clamp(0.0, 1.0); final currentChapter = ref.watch(currentPlayingChapterProvider); final currentBookMetadata = ref.watch(currentBookMetadataProvider); @@ -49,15 +46,11 @@ class PlayerWhenExpanded extends HookConsumerWidget { return Column( children: [ // sized box for system status bar; not needed as not full screen - SizedBox( - height: MediaQuery.of(context).padding.top * earlyPercentage, - ), + SizedBox(height: MediaQuery.of(context).padding.top * earlyPercentage), // a row with a down arrow to minimize the player, a pill shaped container to drag the player, and a cast button ConstrainedBox( - constraints: BoxConstraints( - maxHeight: 100 * earlyPercentage, - ), + constraints: BoxConstraints(maxHeight: 100 * earlyPercentage), child: Opacity( opacity: earlyPercentage, child: Padding( @@ -104,8 +97,9 @@ class PlayerWhenExpanded extends HookConsumerWidget { decoration: BoxDecoration( boxShadow: [ BoxShadow( - color: - Theme.of(context).colorScheme.primary.withOpacity(0.1), + color: Theme.of( + context, + ).colorScheme.primary.withValues(alpha: 0.1), blurRadius: 32 * earlyPercentage, spreadRadius: 8 * earlyPercentage, // offset: Offset(0, 16 * earlyPercentage), @@ -168,11 +162,10 @@ class PlayerWhenExpanded extends HookConsumerWidget { currentBookMetadata?.authorName ?? '', ].join(' - '), style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.7), - ), + color: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.7), + ), maxLines: 1, overflow: TextOverflow.ellipsis, ), diff --git a/lib/features/player/view/player_when_minimized.dart b/lib/features/player/view/player_when_minimized.dart index 435a6de..9729fe2 100644 --- a/lib/features/player/view/player_when_minimized.dart +++ b/lib/features/player/view/player_when_minimized.dart @@ -32,8 +32,10 @@ class PlayerWhenMinimized extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final player = ref.watch(audiobookPlayerProvider); final vanishingPercentage = 1 - percentageMiniplayer; - final progress = - useStream(player.slowPositionStream, initialData: Duration.zero); + final progress = useStream( + player.slowPositionStream, + initialData: Duration.zero, + ); final bookMetaExpanded = ref.watch(currentBookMetadataProvider); @@ -61,9 +63,7 @@ class PlayerWhenMinimized extends HookConsumerWidget { ); }, child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: maxImgSize, - ), + constraints: BoxConstraints(maxWidth: maxImgSize), child: imgWidget, ), ), @@ -80,7 +80,8 @@ class PlayerWhenMinimized extends HookConsumerWidget { // AutoScrollText( Text( bookMetaExpanded?.title ?? '', - maxLines: 1, overflow: TextOverflow.ellipsis, + maxLines: 1, + overflow: TextOverflow.ellipsis, // velocity: // const Velocity(pixelsPerSecond: Offset(16, 0)), style: Theme.of(context).textTheme.bodyLarge, @@ -90,11 +91,10 @@ class PlayerWhenMinimized extends HookConsumerWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.7), - ), + color: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.7), + ), ), ], ), @@ -135,7 +135,8 @@ class PlayerWhenMinimized extends HookConsumerWidget { SizedBox( height: barHeight, child: LinearProgressIndicator( - value: (progress.data ?? Duration.zero).inSeconds / + value: + (progress.data ?? Duration.zero).inSeconds / player.book!.duration.inSeconds, color: Theme.of(context).colorScheme.onPrimaryContainer, backgroundColor: Theme.of(context).colorScheme.primaryContainer, diff --git a/lib/features/player/view/widgets/audiobook_player_seek_button.dart b/lib/features/player/view/widgets/audiobook_player_seek_button.dart index ac1ec20..d1d1edc 100644 --- a/lib/features/player/view/widgets/audiobook_player_seek_button.dart +++ b/lib/features/player/view/widgets/audiobook_player_seek_button.dart @@ -4,10 +4,7 @@ import 'package:vaani/constants/sizes.dart'; import 'package:vaani/features/player/providers/audiobook_player.dart'; class AudiobookPlayerSeekButton extends HookConsumerWidget { - const AudiobookPlayerSeekButton({ - super.key, - required this.isForward, - }); + const AudiobookPlayerSeekButton({super.key, required this.isForward}); /// if true, the button seeks forward, else it seeks backwards final bool isForward; diff --git a/lib/features/player/view/widgets/audiobook_player_seek_chapter_button.dart b/lib/features/player/view/widgets/audiobook_player_seek_chapter_button.dart index ad47e8b..16127ad 100644 --- a/lib/features/player/view/widgets/audiobook_player_seek_chapter_button.dart +++ b/lib/features/player/view/widgets/audiobook_player_seek_chapter_button.dart @@ -5,10 +5,7 @@ import 'package:vaani/constants/sizes.dart'; import 'package:vaani/features/player/providers/audiobook_player.dart'; class AudiobookPlayerSeekChapterButton extends HookConsumerWidget { - const AudiobookPlayerSeekChapterButton({ - super.key, - required this.isForward, - }); + const AudiobookPlayerSeekChapterButton({super.key, required this.isForward}); /// if true, the button seeks forward, else it seeks backwards final bool isForward; @@ -27,9 +24,7 @@ class AudiobookPlayerSeekChapterButton extends HookConsumerWidget { void seekForward() { final index = player.book!.chapters.indexOf(player.currentChapter!); if (index < player.book!.chapters.length - 1) { - player.seek( - player.book!.chapters[index + 1].start + offset, - ); + player.seek(player.book!.chapters[index + 1].start + offset); } else { player.seek(player.currentChapter!.end); } @@ -37,8 +32,9 @@ class AudiobookPlayerSeekChapterButton extends HookConsumerWidget { /// seek backward to the previous chapter or the start of the current chapter void seekBackward() { - final currentPlayingChapterIndex = - player.book!.chapters.indexOf(player.currentChapter!); + final currentPlayingChapterIndex = player.book!.chapters.indexOf( + player.currentChapter!, + ); final chapterPosition = player.positionInBook - player.currentChapter!.start; BookChapter chapterToSeekTo; @@ -49,9 +45,7 @@ class AudiobookPlayerSeekChapterButton extends HookConsumerWidget { } else { chapterToSeekTo = player.currentChapter!; } - player.seek( - chapterToSeekTo.start + offset, - ); + player.seek(chapterToSeekTo.start + offset); } return IconButton( diff --git a/lib/features/player/view/widgets/chapter_selection_button.dart b/lib/features/player/view/widgets/chapter_selection_button.dart index d4da604..ea2154e 100644 --- a/lib/features/player/view/widgets/chapter_selection_button.dart +++ b/lib/features/player/view/widgets/chapter_selection_button.dart @@ -1,17 +1,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:vaani/features/player/providers/audiobook_player.dart'; -import 'package:vaani/features/player/providers/currently_playing_provider.dart'; -import 'package:vaani/features/player/view/player_when_expanded.dart'; -import 'package:vaani/shared/extensions/chapter.dart'; -import 'package:vaani/shared/extensions/duration_format.dart'; -import 'package:vaani/shared/hooks.dart'; +import 'package:vaani/features/player/providers/audiobook_player.dart' + show audiobookPlayerProvider; +import 'package:vaani/features/player/providers/currently_playing_provider.dart' + show currentPlayingChapterProvider, currentlyPlayingBookProvider; +import 'package:vaani/features/player/view/player_when_expanded.dart' + show pendingPlayerModals; +import 'package:vaani/features/player/view/widgets/playing_indicator_icon.dart'; +import 'package:vaani/main.dart' show appLogger; +import 'package:vaani/shared/extensions/chapter.dart' show ChapterDuration; +import 'package:vaani/shared/extensions/duration_format.dart' + show DurationFormat; +import 'package:vaani/shared/hooks.dart' show useTimer; class ChapterSelectionButton extends HookConsumerWidget { - const ChapterSelectionButton({ - super.key, - }); + const ChapterSelectionButton({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -43,9 +47,7 @@ class ChapterSelectionButton extends HookConsumerWidget { } class ChapterSelectionModal extends HookConsumerWidget { - const ChapterSelectionModal({ - super.key, - }); + const ChapterSelectionModal({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -55,7 +57,7 @@ class ChapterSelectionModal extends HookConsumerWidget { final currentChapterIndex = currentChapter?.id; final chapterKey = GlobalKey(); scrollToCurrentChapter() async { - debugPrint('scrolling to chapter'); + appLogger.fine('scrolling to chapter'); await Scrollable.ensureVisible( chapterKey.currentContext!, duration: 200.ms, @@ -66,6 +68,7 @@ class ChapterSelectionModal extends HookConsumerWidget { useTimer(scrollToCurrentChapter, 500.ms); // useInterval(scrollToCurrentChapter, 500.ms); + final theme = Theme.of(context); return Column( children: [ ListTile( @@ -80,24 +83,40 @@ class ChapterSelectionModal extends HookConsumerWidget { child: currentBook?.chapters == null ? const Text('No chapters found') : Column( - children: [ - for (final chapter in currentBook!.chapters) - ListTile( - title: Text(chapter.title), - trailing: Text( - '(${chapter.duration.smartBinaryFormat})', - ), - selected: currentChapterIndex == chapter.id, - key: currentChapterIndex == chapter.id - ? chapterKey + children: currentBook!.chapters.map((chapter) { + final isCurrent = currentChapterIndex == chapter.id; + final isPlayed = + currentChapterIndex != null && + chapter.id < currentChapterIndex; + return ListTile( + autofocus: isCurrent, + iconColor: isPlayed && !isCurrent + ? theme.disabledColor + : null, + title: Text( + chapter.title, + style: isPlayed && !isCurrent + ? TextStyle(color: theme.disabledColor) : null, - onTap: () { - Navigator.of(context).pop(); - notifier.seek(chapter.start + 90.ms); - notifier.play(); - }, ), - ], + subtitle: Text( + '(${chapter.duration.smartBinaryFormat})', + style: isPlayed && !isCurrent + ? TextStyle(color: theme.disabledColor) + : null, + ), + trailing: isCurrent + ? const PlayingIndicatorIcon() + : const Icon(Icons.play_arrow), + selected: isCurrent, + key: isCurrent ? chapterKey : null, + onTap: () { + Navigator.of(context).pop(); + notifier.seek(chapter.start + 90.ms); + notifier.play(); + }, + ); + }).toList(), ), ), ), diff --git a/lib/features/player/view/widgets/player_speed_adjust_button.dart b/lib/features/player/view/widgets/player_speed_adjust_button.dart index 36b7cd7..021f999 100644 --- a/lib/features/player/view/widgets/player_speed_adjust_button.dart +++ b/lib/features/player/view/widgets/player_speed_adjust_button.dart @@ -10,9 +10,7 @@ import 'package:vaani/settings/app_settings_provider.dart'; final _logger = Logger('PlayerSpeedAdjustButton'); class PlayerSpeedAdjustButton extends HookConsumerWidget { - const PlayerSpeedAdjustButton({ - super.key, - }); + const PlayerSpeedAdjustButton({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,21 +33,19 @@ class PlayerSpeedAdjustButton extends HookConsumerWidget { notifier.setSpeed(speed); if (appSettings.playerSettings.configurePlayerForEveryBook) { ref - .read( - bookSettingsProvider(bookId).notifier, - ) + .read(bookSettingsProvider(bookId).notifier) .update( - bookSettings.copyWith - .playerSettings(preferredDefaultSpeed: speed), + bookSettings.copyWith.playerSettings( + preferredDefaultSpeed: speed, + ), ); } else { ref - .read( - appSettingsProvider.notifier, - ) + .read(appSettingsProvider.notifier) .update( - appSettings.copyWith - .playerSettings(preferredDefaultSpeed: speed), + appSettings.copyWith.playerSettings( + preferredDefaultSpeed: speed, + ), ); } }, diff --git a/lib/features/player/view/widgets/playing_indicator_icon.dart b/lib/features/player/view/widgets/playing_indicator_icon.dart new file mode 100644 index 0000000..35c47fc --- /dev/null +++ b/lib/features/player/view/widgets/playing_indicator_icon.dart @@ -0,0 +1,194 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; + +/// An icon that animates like audio equalizer bars to indicate playback. +/// +/// Creates multiple vertical bars that independently animate their height +/// in a looping, visually dynamic pattern. +class PlayingIndicatorIcon extends StatefulWidget { + /// The number of vertical bars in the indicator. + final int barCount; + + /// The total width and height of the icon area. + final double size; + + /// The color of the bars. Defaults to the current [IconTheme] color. + final Color? color; + + /// The minimum height factor for a bar (relative to [size]). + /// When [centerSymmetric] is true, this represents the minimum height + /// extending from the center line (so total minimum height is 2 * minHeightFactor * size). + /// When false, it's the minimum height from the bottom. + final double minHeightFactor; + + /// The maximum height factor for a bar (relative to [size]). + /// When [centerSymmetric] is true, this represents the maximum height + /// extending from the center line (so total maximum height is 2 * maxHeightFactor * size). + /// When false, it's the maximum height from the bottom. + final double maxHeightFactor; + + /// Base duration for a full up/down animation cycle for a single bar. + /// Actual duration will vary slightly per bar. + final Duration baseCycleDuration; + + /// If true, the bars animate symmetrically expanding/collapsing from the + /// horizontal center line. If false (default), they expand/collapse from + /// the bottom edge. + final bool centerSymmetric; + + const PlayingIndicatorIcon({ + super.key, + this.barCount = 4, + this.size = 20.0, + this.color, + this.minHeightFactor = 0.2, + this.maxHeightFactor = 1.0, + this.baseCycleDuration = const Duration(milliseconds: 350), + this.centerSymmetric = true, + }); + + @override + State createState() => _PlayingIndicatorIconState(); +} + +class _PlayingIndicatorIconState extends State { + late List<_BarAnimationParams> _animationParams; + final _random = Random(); + + @override + void initState() { + super.initState(); + _animationParams = List.generate( + widget.barCount, + _createRandomParams, + growable: false, + ); + } + + // Helper to generate random parameters for one bar's animation cycle + _BarAnimationParams _createRandomParams(int index) { + final duration1 = + (widget.baseCycleDuration * (0.8 + _random.nextDouble() * 0.4)); + final duration2 = + (widget.baseCycleDuration * (0.8 + _random.nextDouble() * 0.4)); + + // Note: These factors represent the scale relative to the *half-height* + // if centerSymmetric is true, controlled by the alignment in scaleY. + final targetHeightFactor1 = + widget.minHeightFactor + + _random.nextDouble() * + (widget.maxHeightFactor - widget.minHeightFactor); + final targetHeightFactor2 = + widget.minHeightFactor + + _random.nextDouble() * + (widget.maxHeightFactor - widget.minHeightFactor); + + // --- Random initial delay --- + final initialDelay = + (_random.nextDouble() * (widget.baseCycleDuration.inMilliseconds / 4)) + .ms; + + return _BarAnimationParams( + duration1: duration1, + duration2: duration2, + targetHeightFactor1: targetHeightFactor1, + targetHeightFactor2: targetHeightFactor2, + initialDelay: initialDelay, + ); + } + + @override + Widget build(BuildContext context) { + final color = + widget.color ?? + IconTheme.of(context).color ?? + Theme.of(context).colorScheme.primary; + + // --- Bar geometry calculation --- + final double totalSpacing = widget.size * 0.2; + // Ensure at least 1px spacing if size is very small + final double barSpacing = max(1.0, totalSpacing / (widget.barCount + 1)); + final double availableWidthForBars = + widget.size - (barSpacing * (widget.barCount + 1)); + final double barWidth = max(1.0, availableWidthForBars / widget.barCount); + // Max height remains the full size potential for the container + final double maxHeight = widget.size; + + // Determine the alignment for scaling based on the symmetric flag + final Alignment scaleAlignment = widget.centerSymmetric + ? Alignment.center + : Alignment.bottomCenter; + + // Determine the cross axis alignment for the Row + final CrossAxisAlignment rowAlignment = widget.centerSymmetric + ? CrossAxisAlignment.center + : CrossAxisAlignment.end; + + return SizedBox( + width: widget.size, + height: widget.size, + // Clip ensures bars don't draw outside the SizedBox bounds + // especially important for center alignment if maxFactor > 0.5 + child: ClipRect( + child: Row( + // Use calculated alignment + crossAxisAlignment: rowAlignment, + // Use spaceEvenly for better distribution, especially with center alignment + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: List.generate(widget.barCount, (index) { + final params = _animationParams[index]; + // The actual bar widget that will be animated + return Container( + width: barWidth, + // Set initial height to the max potential height + // The scaleY animation will control the visible height + height: maxHeight, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(barWidth / 2), + ), + ) + .animate( + delay: params.initialDelay, + onPlay: (controller) => controller.repeat(reverse: true), + ) + // 1. Scale to targetHeightFactor1 + .scaleY( + begin: widget.minHeightFactor, // Scale factor starts near min + end: params.targetHeightFactor1, + duration: params.duration1, + curve: Curves.easeInOutCirc, + alignment: scaleAlignment, // Apply chosen alignment + ) + // 2. Then scale to targetHeightFactor2 + .then() + .scaleY( + end: params.targetHeightFactor2, + duration: params.duration2, + curve: Curves.easeInOutCirc, + alignment: scaleAlignment, // Apply chosen alignment + ); + }, growable: false), + ), + ), + ); + } +} + +// Helper class: Renamed height fields for clarity +class _BarAnimationParams { + final Duration duration1; + final Duration duration2; + final double targetHeightFactor1; // Factor relative to total size + final double targetHeightFactor2; // Factor relative to total size + final Duration initialDelay; + + _BarAnimationParams({ + required this.duration1, + required this.duration2, + required this.targetHeightFactor1, + required this.targetHeightFactor2, + required this.initialDelay, + }); +} diff --git a/lib/features/player/view/widgets/speed_selector.dart b/lib/features/player/view/widgets/speed_selector.dart index 65a3bfa..f14604a 100644 --- a/lib/features/player/view/widgets/speed_selector.dart +++ b/lib/features/player/view/widgets/speed_selector.dart @@ -6,15 +6,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:list_wheel_scroll_view_nls/list_wheel_scroll_view_nls.dart'; import 'package:vaani/features/player/providers/audiobook_player.dart'; import 'package:vaani/settings/app_settings_provider.dart'; -import 'package:vaani/shared/hooks.dart'; const double itemExtent = 25; class SpeedSelector extends HookConsumerWidget { - const SpeedSelector({ - super.key, - required this.onSpeedSelected, - }); + const SpeedSelector({super.key, required this.onSpeedSelected}); final void Function(double speed) onSpeedSelected; @@ -27,34 +23,22 @@ class SpeedSelector extends HookConsumerWidget { final speedState = useState(currentSpeed); // hook the onSpeedSelected function to the state - useEffect( - () { - onSpeedSelected(speedState.value); - return null; - }, - [speedState.value], - ); + useEffect(() { + onSpeedSelected(speedState.value); + return null; + }, [speedState.value]); // the speed options - final minSpeed = min( - speeds.reduce(min), - playerSettings.minSpeed, - ); - final maxSpeed = max( - speeds.reduce(max), - playerSettings.maxSpeed, - ); + final minSpeed = min(speeds.reduce(min), playerSettings.minSpeed); + final maxSpeed = max(speeds.reduce(max), playerSettings.maxSpeed); final speedIncrement = playerSettings.speedIncrement; final availableSpeeds = ((maxSpeed - minSpeed) / speedIncrement).ceil() + 1; - final availableSpeedsList = List.generate( - availableSpeeds, - (index) { - // need to round to 2 decimal place to avoid floating point errors - return double.parse( - (minSpeed + index * speedIncrement).toStringAsFixed(2), - ); - }, - ); + final availableSpeedsList = List.generate(availableSpeeds, (index) { + // need to round to 2 decimal place to avoid floating point errors + return double.parse( + (minSpeed + index * speedIncrement).toStringAsFixed(2), + ); + }); final scrollController = useFixedExtentScrollController( initialItem: availableSpeedsList.indexOf(currentSpeed), @@ -108,18 +92,19 @@ class SpeedSelector extends HookConsumerWidget { (speed) => TextButton( style: speed == speedState.value ? TextButton.styleFrom( - backgroundColor: - Theme.of(context).colorScheme.primaryContainer, - foregroundColor: Theme.of(context) - .colorScheme - .onPrimaryContainer, + backgroundColor: Theme.of( + context, + ).colorScheme.primaryContainer, + foregroundColor: Theme.of( + context, + ).colorScheme.onPrimaryContainer, ) // border if not selected : TextButton.styleFrom( side: BorderSide( - color: Theme.of(context) - .colorScheme - .primaryContainer, + color: Theme.of( + context, + ).colorScheme.primaryContainer, ), ), onPressed: () async { @@ -196,14 +181,13 @@ class SpeedWheel extends StatelessWidget { controller: scrollController, scrollDirection: Axis.horizontal, itemExtent: itemExtent, - diameterRatio: 1.5, squeeze: 1.2, + diameterRatio: 1.5, + squeeze: 1.2, // useMagnifier: true, // magnification: 1.5, physics: const FixedExtentScrollPhysics(), children: availableSpeedsList - .map( - (speed) => SpeedLine(speed: speed), - ) + .map((speed) => SpeedLine(speed: speed)) .toList(), onSelectedItemChanged: (index) { speedState.value = availableSpeedsList[index]; @@ -233,10 +217,7 @@ class SpeedWheel extends StatelessWidget { } class SpeedLine extends StatelessWidget { - const SpeedLine({ - super.key, - required this.speed, - }); + const SpeedLine({super.key, required this.speed}); final double speed; @@ -251,8 +232,8 @@ class SpeedLine extends StatelessWidget { width: speed % 0.5 == 0 ? 3 : speed % 0.25 == 0 - ? 2 - : 0.5, + ? 2 + : 0.5, color: Theme.of(context).colorScheme.onSurface, ), ), diff --git a/lib/features/shake_detection/core/shake_detector.dart b/lib/features/shake_detection/core/shake_detector.dart index 023a8ab..effa131 100644 --- a/lib/features/shake_detection/core/shake_detector.dart +++ b/lib/features/shake_detection/core/shake_detector.dart @@ -29,7 +29,7 @@ class ShakeDetector { DateTime _lastShakeTime = DateTime.now(); final StreamController - _detectedShakeStreamController = StreamController.broadcast(); + _detectedShakeStreamController = StreamController.broadcast(); void start() { if (_accelerometerSubscription != null) { @@ -37,26 +37,27 @@ class ShakeDetector { return; } _accelerometerSubscription = - userAccelerometerEventStream(samplingPeriod: _settings.samplingPeriod) - .listen((event) { - _logger.finest('RMS: ${event.rms}'); - if (event.rms > _settings.threshold) { - _currentShakeCount++; + userAccelerometerEventStream( + samplingPeriod: _settings.samplingPeriod, + ).listen((event) { + _logger.finest('RMS: ${event.rms}'); + if (event.rms > _settings.threshold) { + _currentShakeCount++; - if (_currentShakeCount >= _settings.shakeTriggerCount && - !isCoolDownNeeded()) { - _logger.fine('Shake detected $_currentShakeCount times'); + if (_currentShakeCount >= _settings.shakeTriggerCount && + !isCoolDownNeeded()) { + _logger.fine('Shake detected $_currentShakeCount times'); - onShakeDetected?.call(); - _detectedShakeStreamController.add(event); + onShakeDetected?.call(); + _detectedShakeStreamController.add(event); - _lastShakeTime = DateTime.now(); - _currentShakeCount = 0; - } - } else { - _currentShakeCount = 0; - } - }); + _lastShakeTime = DateTime.now(); + _currentShakeCount = 0; + } + } else { + _currentShakeCount = 0; + } + }); _logger.fine('ShakeDetector started'); } diff --git a/lib/features/shake_detection/providers/shake_detector.dart b/lib/features/shake_detection/providers/shake_detector.dart index 8892a92..fa17920 100644 --- a/lib/features/shake_detection/providers/shake_detector.dart +++ b/lib/features/shake_detection/providers/shake_detector.dart @@ -59,34 +59,29 @@ class ShakeDetector extends _$ShakeDetector { final sleepTimer = ref.watch(sleepTimerProvider); if (!shakeDetectionSettings.shakeAction.isPlaybackManagementEnabled && sleepTimer == null) { - _logger - .config('No playback management is enabled and sleep timer is off, ' - 'so shake detection is disabled'); + _logger.config( + 'No playback management is enabled and sleep timer is off, ' + 'so shake detection is disabled', + ); return null; } _logger.config('Creating shake detector'); - final detector = core.ShakeDetector( - shakeDetectionSettings, - () { - final wasActionComplete = doShakeAction( - shakeDetectionSettings.shakeAction, - ref: ref, - ); - if (wasActionComplete) { - shakeDetectionSettings.feedback.forEach(postShakeFeedback); - } - }, - ); + final detector = core.ShakeDetector(shakeDetectionSettings, () { + final wasActionComplete = doShakeAction( + shakeDetectionSettings.shakeAction, + ref: ref, + ); + if (wasActionComplete) { + shakeDetectionSettings.feedback.forEach(postShakeFeedback); + } + }); ref.onDispose(detector.dispose); return detector; } /// Perform the shake action and return whether the action was successful - bool doShakeAction( - ShakeAction shakeAction, { - required Ref ref, - }) { + bool doShakeAction(ShakeAction shakeAction, {required Ref ref}) { final player = ref.read(simpleAudiobookPlayerProvider); if (player.book == null && shakeAction.isPlaybackManagementEnabled) { _logger.warning('No book is loaded'); @@ -166,8 +161,11 @@ extension on ShakeAction { } bool get isPlaybackManagementEnabled { - return {ShakeAction.playPause, ShakeAction.fastForward, ShakeAction.rewind} - .contains(this); + return { + ShakeAction.playPause, + ShakeAction.fastForward, + ShakeAction.rewind, + }.contains(this); } bool get shouldActOnSleepTimer { diff --git a/lib/features/shake_detection/providers/shake_detector.g.dart b/lib/features/shake_detection/providers/shake_detector.g.dart index 0f4285f..669d6d5 100644 --- a/lib/features/shake_detection/providers/shake_detector.g.dart +++ b/lib/features/shake_detection/providers/shake_detector.g.dart @@ -6,21 +6,57 @@ part of 'shake_detector.dart'; // RiverpodGenerator // ************************************************************************** +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(ShakeDetector) +final shakeDetectorProvider = ShakeDetectorProvider._(); + +final class ShakeDetectorProvider + extends $NotifierProvider { + ShakeDetectorProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'shakeDetectorProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$shakeDetectorHash(); + + @$internal + @override + ShakeDetector create() => ShakeDetector(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(core.ShakeDetector? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + String _$shakeDetectorHash() => r'2a380bab1d4021d05d2ae40fec964a5f33d3730c'; -/// See also [ShakeDetector]. -@ProviderFor(ShakeDetector) -final shakeDetectorProvider = - AutoDisposeNotifierProvider.internal( - ShakeDetector.new, - name: r'shakeDetectorProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$shakeDetectorHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$ShakeDetector = AutoDisposeNotifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +abstract class _$ShakeDetector extends $Notifier { + core.ShakeDetector? build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + core.ShakeDetector?, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/features/sleep_timer/core/sleep_timer.dart b/lib/features/sleep_timer/core/sleep_timer.dart index 9350436..b774062 100644 --- a/lib/features/sleep_timer/core/sleep_timer.dart +++ b/lib/features/sleep_timer/core/sleep_timer.dart @@ -69,7 +69,7 @@ class SleepTimer { }), ); - _logger.fine('created with duration: $duration'); + _logger.info('created with duration: $duration'); } /// resets the timer and stops it @@ -90,13 +90,11 @@ class SleepTimer { if (player.playing) { startCountDown(); } - _logger.fine('restarted timer'); + _logger.info('restarted timer'); } /// starts the timer with the given duration or the default duration - void startCountDown([ - Duration? forDuration, - ]) { + void startCountDown([Duration? forDuration]) { clearCountDownTimer(); duration = forDuration ?? duration; timer = Timer(duration, () { @@ -105,7 +103,7 @@ class SleepTimer { _logger.fine('paused player after $duration'); }); startedAt = DateTime.now(); - _logger.fine('started for $duration at $startedAt'); + _logger.info('started for $duration at $startedAt'); } Duration get remainingTime { @@ -130,6 +128,6 @@ class SleepTimer { for (var sub in _subscriptions) { sub.cancel(); } - _logger.fine('disposed'); + _logger.info('disposed'); } } diff --git a/lib/features/sleep_timer/providers/sleep_timer_provider.g.dart b/lib/features/sleep_timer/providers/sleep_timer_provider.g.dart index 7daade0..a3b0e3e 100644 --- a/lib/features/sleep_timer/providers/sleep_timer_provider.g.dart +++ b/lib/features/sleep_timer/providers/sleep_timer_provider.g.dart @@ -6,20 +6,57 @@ part of 'sleep_timer_provider.dart'; // RiverpodGenerator // ************************************************************************** +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(SleepTimer) +final sleepTimerProvider = SleepTimerProvider._(); + +final class SleepTimerProvider + extends $NotifierProvider { + SleepTimerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'sleepTimerProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$sleepTimerHash(); + + @$internal + @override + SleepTimer create() => SleepTimer(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(core.SleepTimer? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + String _$sleepTimerHash() => r'2679454a217d0630a833d730557ab4e4feac2e56'; -/// See also [SleepTimer]. -@ProviderFor(SleepTimer) -final sleepTimerProvider = - NotifierProvider.internal( - SleepTimer.new, - name: r'sleepTimerProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$sleepTimerHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$SleepTimer = Notifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +abstract class _$SleepTimer extends $Notifier { + core.SleepTimer? build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + core.SleepTimer?, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/features/sleep_timer/view/sleep_timer_button.dart b/lib/features/sleep_timer/view/sleep_timer_button.dart index 075b0e4..0f33b72 100644 --- a/lib/features/sleep_timer/view/sleep_timer_button.dart +++ b/lib/features/sleep_timer/view/sleep_timer_button.dart @@ -11,12 +11,9 @@ import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart' import 'package:vaani/main.dart'; import 'package:vaani/settings/app_settings_provider.dart'; import 'package:vaani/shared/extensions/duration_format.dart'; -import 'package:vaani/shared/hooks.dart'; class SleepTimerButton extends HookConsumerWidget { - const SleepTimerButton({ - super.key, - }); + const SleepTimerButton({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -48,8 +45,9 @@ class SleepTimerButton extends HookConsumerWidget { ); pendingPlayerModals--; ref.read(sleepTimerProvider.notifier).setTimer(durationState.value); - appLogger - .fine('Sleep Timer dialog closed with ${durationState.value}'); + appLogger.fine( + 'Sleep Timer dialog closed with ${durationState.value}', + ); }, child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), @@ -58,9 +56,7 @@ class SleepTimerButton extends HookConsumerWidget { Symbols.bedtime, color: Theme.of(context).colorScheme.onSurface, ) - : RemainingSleepTimeDisplay( - timer: sleepTimer, - ), + : RemainingSleepTimeDisplay(timer: sleepTimer), ), ), ); @@ -68,10 +64,7 @@ class SleepTimerButton extends HookConsumerWidget { } class SleepTimerBottomSheet extends HookConsumerWidget { - const SleepTimerBottomSheet({ - super.key, - this.onDurationSelected, - }); + const SleepTimerBottomSheet({super.key, this.onDurationSelected}); final void Function(Duration?)? onDurationSelected; @@ -92,8 +85,9 @@ class SleepTimerBottomSheet extends HookConsumerWidget { ]; final scrollController = useFixedExtentScrollController( - initialItem: - allPossibleDurations.indexOf(sleepTimer?.duration ?? minDuration), + initialItem: allPossibleDurations.indexOf( + sleepTimer?.duration ?? minDuration, + ), ); final durationState = useState( @@ -101,13 +95,10 @@ class SleepTimerBottomSheet extends HookConsumerWidget { ); // useEffect to rebuild the sleep timer when the duration changes - useEffect( - () { - onDurationSelected?.call(durationState.value); - return null; - }, - [durationState.value], - ); + useEffect(() { + onDurationSelected?.call(durationState.value); + return null; + }, [durationState.value]); return Column( mainAxisSize: MainAxisSize.min, @@ -172,18 +163,19 @@ class SleepTimerBottomSheet extends HookConsumerWidget { (timerDuration) => TextButton( style: timerDuration == durationState.value ? TextButton.styleFrom( - backgroundColor: - Theme.of(context).colorScheme.primaryContainer, - foregroundColor: Theme.of(context) - .colorScheme - .onPrimaryContainer, + backgroundColor: Theme.of( + context, + ).colorScheme.primaryContainer, + foregroundColor: Theme.of( + context, + ).colorScheme.onPrimaryContainer, ) // border if not selected : TextButton.styleFrom( side: BorderSide( - color: Theme.of(context) - .colorScheme - .primaryContainer, + color: Theme.of( + context, + ).colorScheme.primaryContainer, ), ), onPressed: () async { @@ -216,10 +208,7 @@ class SleepTimerBottomSheet extends HookConsumerWidget { } class RemainingSleepTimeDisplay extends HookConsumerWidget { - const RemainingSleepTimeDisplay({ - super.key, - required this.timer, - }); + const RemainingSleepTimeDisplay({super.key, required this.timer}); final SleepTimer timer; @@ -231,17 +220,14 @@ class RemainingSleepTimeDisplay extends HookConsumerWidget { color: Theme.of(context).colorScheme.primary, borderRadius: BorderRadius.circular(16), ), - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Text( timer.timer == null ? timer.duration.smartBinaryFormat : remainingTime?.smartBinaryFormat ?? '', style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.onPrimary, - ), + color: Theme.of(context).colorScheme.onPrimary, + ), ), ); } @@ -273,8 +259,9 @@ class SleepTimerWheel extends StatelessWidget { icon: const Icon(Icons.remove), onPressed: () { // animate to index - 1 - final index = availableDurations - .indexOf(durationState.value ?? Duration.zero); + final index = availableDurations.indexOf( + durationState.value ?? Duration.zero, + ); if (index > 0) { scrollController.animateToItem( index - 1, @@ -290,14 +277,13 @@ class SleepTimerWheel extends StatelessWidget { controller: scrollController, scrollDirection: Axis.horizontal, itemExtent: itemExtent, - diameterRatio: 1.5, squeeze: 1.2, + diameterRatio: 1.5, + squeeze: 1.2, // useMagnifier: true, // magnification: 1.5, physics: const FixedExtentScrollPhysics(), children: availableDurations - .map( - (duration) => DurationLine(duration: duration), - ) + .map((duration) => DurationLine(duration: duration)) .toList(), onSelectedItemChanged: (index) { durationState.value = availableDurations[index]; @@ -311,8 +297,9 @@ class SleepTimerWheel extends StatelessWidget { icon: const Icon(Icons.add), onPressed: () { // animate to index + 1 - final index = availableDurations - .indexOf(durationState.value ?? Duration.zero); + final index = availableDurations.indexOf( + durationState.value ?? Duration.zero, + ); if (index < availableDurations.length - 1) { scrollController.animateToItem( index + 1, @@ -328,10 +315,7 @@ class SleepTimerWheel extends StatelessWidget { } class DurationLine extends StatelessWidget { - const DurationLine({ - super.key, - required this.duration, - }); + const DurationLine({super.key, required this.duration}); final Duration duration; @@ -346,8 +330,8 @@ class DurationLine extends StatelessWidget { width: duration.inMinutes % 5 == 0 ? 3 : duration.inMinutes % 2.5 == 0 - ? 2 - : 0.5, + ? 2 + : 0.5, color: Theme.of(context).colorScheme.onSurface, ), ), diff --git a/lib/features/you/view/server_manager.dart b/lib/features/you/view/server_manager.dart index 2ab70ea..f8d13b0 100644 --- a/lib/features/you/view/server_manager.dart +++ b/lib/features/you/view/server_manager.dart @@ -2,452 +2,333 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:vaani/api/api_provider.dart'; -import 'package:vaani/api/authenticated_user_provider.dart'; -import 'package:vaani/api/server_provider.dart'; -import 'package:vaani/models/error_response.dart'; -import 'package:vaani/router/router.dart'; -import 'package:vaani/settings/api_settings_provider.dart'; +import 'package:vaani/api/api_provider.dart' show makeBaseUrl; +import 'package:vaani/api/authenticated_users_provider.dart' + show authenticatedUsersProvider; +import 'package:vaani/api/server_provider.dart' + show ServerAlreadyExistsException, audiobookShelfServerProvider; +import 'package:vaani/features/onboarding/view/user_login.dart' + show UserLoginWidget; +import 'package:vaani/features/player/view/mini_player_bottom_padding.dart' + show MiniPlayerBottomPadding; +import 'package:vaani/main.dart' show appLogger; +import 'package:vaani/router/router.dart' show Routes; +import 'package:vaani/settings/api_settings_provider.dart' + show apiSettingsProvider; import 'package:vaani/settings/models/models.dart' as model; -import 'package:vaani/shared/widgets/add_new_server.dart'; +import 'package:vaani/shared/extensions/obfuscation.dart' show ObfuscateSet; +import 'package:vaani/shared/widgets/add_new_server.dart' show AddNewServer; class ServerManagerPage extends HookConsumerWidget { - const ServerManagerPage({ - super.key, - }); + const ServerManagerPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final apiSettings = ref.watch(apiSettingsProvider); - final registeredServers = ref.watch(audiobookShelfServerProvider); - final registeredServersAsList = registeredServers.toList(); - final availableUsers = ref.watch(authenticatedUserProvider); - final serverURIController = useTextEditingController(); - final formKey = GlobalKey(); - - debugPrint('registered servers: $registeredServers'); - debugPrint('available users: $availableUsers'); return Scaffold( - appBar: AppBar( - title: const Text('Manage Accounts'), - ), + appBar: AppBar(title: const Text('Manage Accounts')), body: Center( child: Padding( padding: const EdgeInsets.all(8.0), - child: Column( - // crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - const Text( - 'Registered Servers', - ), - Expanded( - child: ListView.builder( - itemCount: registeredServers.length, - reverse: true, - itemBuilder: (context, index) { - var registeredServer = registeredServersAsList[index]; - return ExpansionTile( - title: Text(registeredServer.serverUrl.toString()), - subtitle: Text( - 'Users: ${availableUsers.where((element) => element.server == registeredServer).length}', - ), - // trailing: _DeleteServerButton( - // registeredServer: registeredServer, - // ), - // children are list of users of this server - children: availableUsers - .where( - (element) => element.server == registeredServer, - ) - .map( - (e) => ListTile( - selected: apiSettings.activeUser == e, - leading: apiSettings.activeUser == e - ? const Icon(Icons.person) - : const Icon(Icons.person_off_outlined), - title: Text(e.username ?? 'Anonymous'), - onTap: apiSettings.activeUser == e - ? null - : () { - ref - .read(apiSettingsProvider.notifier) - .updateState( - apiSettings.copyWith( - activeUser: e, - ), - ); - // pop all routes and go to the home page - // while (context.canPop()) { - // context.pop(); - // } - context.goNamed( - Routes.home.name, - ); - }, - trailing: IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('Delete User'), - content: const Text( - 'Are you sure you want to delete this user?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - ref - .read( - authenticatedUserProvider - .notifier, - ) - .removeUser(e); - Navigator.of(context).pop(); - }, - child: const Text('Delete'), - ), - ], - ); - }, - ); - }, - ), - ), - ) - .nonNulls - .toList() - - // add buttons of delete server and add user to server at the end - ..addAll([ - ListTile( - leading: const Icon(Icons.person_add), - title: const Text('Add User'), - onTap: () async { - // open a dialog to add a new user with username and password or another method using only auth token - final addedUser = await showDialog( - context: context, - builder: (context) { - return _AddUserDialog( - server: registeredServer, - ); - }, - ); - - // if (addedUser != null) { - // // show a snackbar that the user has been added and ask if change to this user - // ScaffoldMessenger.of(context).showSnackBar( - // SnackBar( - // content: const Text( - // 'User added successfully, do you want to switch to this user?', - // ), - // action: SnackBarAction( - // label: 'Switch', - // onPressed: () { - // // set the active user - // ref - // .read(apiSettingsProvider.notifier) - // .updateState( - // apiSettings.copyWith( - // activeUser: addedUser, - // ), - // ); - - // context.goNamed(Routes.home.name); - // }, - // ), - // ), - // ); - // } - }, - ), - ListTile( - leading: const Icon(Icons.delete), - title: const Text('Delete Server'), - onTap: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('Delete Server'), - content: const Text( - 'Are you sure you want to delete this server and all its users?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - ref - .read( - audiobookShelfServerProvider - .notifier, - ) - .removeServer( - registeredServer, - removeUsers: true, - ); - Navigator.of(context).pop(); - }, - child: const Text('Delete'), - ), - ], - ); - }, - ); - }, - ), - ]), - ); - }, - ), - ), - const SizedBox(height: 20), - const Padding( - padding: EdgeInsets.all(8.0), - child: Text('Add New Server'), - ), - Form( - key: formKey, - autovalidateMode: AutovalidateMode.onUserInteraction, - child: AddNewServer( - controller: serverURIController, - onPressed: () { - if (formKey.currentState!.validate()) { - try { - final newServer = model.AudiobookShelfServer( - serverUrl: makeBaseUrl(serverURIController.text), - ); - ref - .read(audiobookShelfServerProvider.notifier) - .addServer( - newServer, - ); - ref.read(apiSettingsProvider.notifier).updateState( - apiSettings.copyWith( - activeServer: newServer, - ), - ); - serverURIController.clear(); - } on ServerAlreadyExistsException catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(e.toString()), - ), - ); - } - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Invalid URL'), - ), - ); - } - }, - ), - ), - ], - ), + child: ServerManagerBody(), ), ), ); } } -class _AddUserDialog extends HookConsumerWidget { - const _AddUserDialog({ - super.key, - required this.server, - }); +class ServerManagerBody extends HookConsumerWidget { + const ServerManagerBody({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final registeredServers = ref.watch(audiobookShelfServerProvider); + final registeredServersAsList = registeredServers.toList(); + final availableUsers = ref.watch(authenticatedUsersProvider); + final apiSettings = ref.watch(apiSettingsProvider); + final serverURIController = useTextEditingController(); + final formKey = GlobalKey(); + + appLogger.fine('registered servers: ${registeredServers.obfuscate()}'); + appLogger.fine('available users: ${availableUsers.obfuscate()}'); + + return Column( + // crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Text('Registered Servers'), + Expanded( + child: ListView.builder( + itemCount: registeredServers.length, + reverse: true, + itemBuilder: (context, index) { + var registeredServer = registeredServersAsList[index]; + return ExpansionTile( + title: Text(registeredServer.serverUrl.toString()), + subtitle: Text( + 'Users: ${availableUsers.where((element) => element.server == registeredServer).length}', + ), + // children are list of users of this server + children: + availableUsers + .where((element) => element.server == registeredServer) + .map((e) => AvailableUserTile(user: e)) + .nonNulls + .toList() + // add buttons of delete server and add user to server at the end + ..addAll([ + AddUserTile(server: registeredServer), + DeleteServerTile(server: registeredServer), + ]), + ); + }, + ), + ), + const SizedBox(height: 20), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text('Add New Server'), + ), + Form( + key: formKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: AddNewServer( + controller: serverURIController, + onPressed: () { + if (formKey.currentState!.validate()) { + try { + final newServer = model.AudiobookShelfServer( + serverUrl: makeBaseUrl(serverURIController.text), + ); + ref + .read(audiobookShelfServerProvider.notifier) + .addServer(newServer); + ref + .read(apiSettingsProvider.notifier) + .updateState( + apiSettings.copyWith(activeServer: newServer), + ); + serverURIController.clear(); + } on ServerAlreadyExistsException catch (e) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toString()))); + } + } else { + ScaffoldMessenger.of( + context, + ).showSnackBar(const SnackBar(content: Text('Invalid URL'))); + } + }, + ), + ), + MiniPlayerBottomPadding(), + ], + ); + } +} + +class DeleteServerTile extends HookConsumerWidget { + const DeleteServerTile({super.key, required this.server}); final model.AudiobookShelfServer server; @override Widget build(BuildContext context, WidgetRef ref) { - final usernameController = useTextEditingController(); - final passwordController = useTextEditingController(); - final authTokensController = useTextEditingController(); - final isPasswordVisible = useState(false); - final apiSettings = ref.watch(apiSettingsProvider); - final isMethodAuth = useState(false); - final api = ref.watch(audiobookshelfApiProvider(server.serverUrl)); - - final formKey = GlobalKey(); - - final serverErrorResponse = ErrorResponseHandler(); - - /// Login to the server and save the user - Future loginAndSave() async { - model.AuthenticatedUser? authenticatedUser; - if (isMethodAuth.value) { - api.token = authTokensController.text; - final success = await api.misc.authorize( - responseErrorHandler: serverErrorResponse.storeError, - ); - if (success != null) { - authenticatedUser = model.AuthenticatedUser( - server: server, - id: success.user.id, - username: success.user.username, - authToken: api.token!, - ); - } - } else { - final username = usernameController.text; - final password = passwordController.text; - final success = await api.login( - username: username, - password: password, - responseErrorHandler: serverErrorResponse.storeError, - ); - if (success != null) { - authenticatedUser = model.AuthenticatedUser( - server: server, - id: success.user.id, - username: username, - authToken: api.token!, - ); - } - } - // add the user to the list of users - if (authenticatedUser != null) { - ref.read(authenticatedUserProvider.notifier).addUser(authenticatedUser); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Login failed. Got response: ${serverErrorResponse.response.body} (${serverErrorResponse.response.statusCode})', - ), - ), - ); - } - return authenticatedUser; - } - - return AlertDialog( - // title: Text('Add User for ${server.serverUrl}'), - title: Text.rich( - TextSpan( - children: [ - TextSpan( - text: 'Add User for ', - style: Theme.of(context).textTheme.labelLarge, - ), - TextSpan( - text: server.serverUrl.toString(), - style: Theme.of(context).textTheme.labelLarge?.copyWith( - color: Theme.of(context).colorScheme.primary, + return ListTile( + leading: const Icon(Icons.delete), + title: const Text('Delete Server'), + onTap: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Remove Server and Users'), + // Make content scrollable in case of smaller screens/keyboard + content: SingleChildScrollView( + child: Text.rich( + TextSpan( + children: [ + const TextSpan(text: 'This will remove the server '), + TextSpan( + text: server.serverUrl.host, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + const TextSpan( + text: ' and all its users\' login info from this app.', + ), + ], ), - ), - ], - ), - ), - content: Form( - key: formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Wrap( - alignment: WrapAlignment.center, - spacing: 8.0, - children: [ - ChoiceChip( - label: const Text('Username/Password'), - selected: !isMethodAuth.value, - onSelected: (selected) { - isMethodAuth.value = !selected; - }, ), - ChoiceChip( - label: const Text('Auth Token'), - selected: isMethodAuth.value, - onSelected: (selected) { - isMethodAuth.value = selected; + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + ref + .read(audiobookShelfServerProvider.notifier) + .removeServer(server, removeUsers: true); + Navigator.of(context).pop(); + }, + child: const Text('Delete'), ), ], - ), - const SizedBox(height: 16), - if (isMethodAuth.value) - TextFormField( - controller: authTokensController, - decoration: const InputDecoration(labelText: 'Auth Token'), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter an auth token'; - } - return null; - }, - ) - else ...[ - TextFormField( - controller: usernameController, - decoration: const InputDecoration(labelText: 'Username'), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter a username'; - } - return null; - }, - ), - TextFormField( - controller: passwordController, - decoration: InputDecoration( - labelText: 'Password', - suffixIcon: IconButton( - icon: Icon( - isPasswordVisible.value - ? Icons.visibility - : Icons.visibility_off, - ), - onPressed: () { - isPasswordVisible.value = !isPasswordVisible.value; - }, - ), - ), - obscureText: !isPasswordVisible.value, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter a password'; - } - return null; - }, - ), - ], - ], - ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); + ); }, - child: const Text('Cancel'), - ), - ElevatedButton( - onPressed: () async { - if (formKey.currentState!.validate()) { - final addedUser = await loginAndSave(); - if (addedUser != null) { - Navigator.of(context).pop(addedUser); - } - } - }, - child: const Text('Add User'), - ), - ], + ); + }, + ); + } +} + +class AddUserTile extends HookConsumerWidget { + const AddUserTile({super.key, required this.server}); + + final model.AudiobookShelfServer server; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ListTile( + leading: const Icon(Icons.person_add), + title: const Text('Add User'), + onTap: () async { + await showDialog( + context: context, + // barrierDismissible: false, // Optional: prevent closing by tapping outside + builder: (dialogContext) { + // Use a different context name to avoid conflicts + return AlertDialog( + title: Text('Add User to ${server.serverUrl.host}'), + // Make content scrollable in case of smaller screens/keyboard + content: SingleChildScrollView( + child: UserLoginWidget( + server: server.serverUrl, + // Pass the callback to pop the dialog on success + onSuccess: (user) { + // Add the user to the server + ref.read(authenticatedUsersProvider.notifier).addUser(user); + Navigator.of(dialogContext).pop(); // Close the dialog + // Optional: Show a confirmation SnackBar + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('User added successfully! Switch?'), + action: SnackBarAction( + label: 'Switch', + onPressed: () { + // Switch to the new user + ref + .read(apiSettingsProvider.notifier) + .updateState( + ref + .read(apiSettingsProvider) + .copyWith(activeUser: user), + ); + context.goNamed(Routes.home.name); + }, + ), + ), + ); + }, + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(dialogContext).pop(); // Close the dialog + }, + child: const Text('Cancel'), + ), + ], + ); + }, + ); + // No need for the SnackBar asking to switch user here anymore. + }, + ); + } +} + +class AvailableUserTile extends HookConsumerWidget { + const AvailableUserTile({super.key, required this.user}); + + final model.AuthenticatedUser user; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final apiSettings = ref.watch(apiSettingsProvider); + + return ListTile( + selected: apiSettings.activeUser == user, + leading: apiSettings.activeUser == user + ? const Icon(Icons.person) + : const Icon(Icons.person_off_outlined), + title: Text(user.username ?? 'Anonymous'), + onTap: apiSettings.activeUser == user + ? null + : () { + ref + .read(apiSettingsProvider.notifier) + .updateState(apiSettings.copyWith(activeUser: user)); + // pop all routes and go to the home page + // while (context.canPop()) { + // context.pop(); + // } + context.goNamed(Routes.home.name); + }, + trailing: IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Remove User Login'), + content: Text.rich( + TextSpan( + children: [ + const TextSpan( + text: 'This will remove login details of the user ', + ), + TextSpan( + text: user.username ?? 'Anonymous', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + const TextSpan(text: ' from this app.'), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + ref + .read(authenticatedUsersProvider.notifier) + .removeUser(user); + Navigator.of(context).pop(); + }, + child: const Text('Delete'), + ), + ], + ); + }, + ); + }, + ), ); } } diff --git a/lib/features/you/view/widgets/library_switch_chip.dart b/lib/features/you/view/widgets/library_switch_chip.dart new file mode 100644 index 0000000..2255c7a --- /dev/null +++ b/lib/features/you/view/widgets/library_switch_chip.dart @@ -0,0 +1,216 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shelfsdk/audiobookshelf_api.dart' show Library; +import 'package:vaani/api/library_provider.dart'; +import 'package:vaani/settings/api_settings_provider.dart' + show apiSettingsProvider; +import 'package:vaani/shared/icons/abs_icons.dart'; +import 'dart:io' show Platform; + +import 'package:flutter/foundation.dart'; +import 'package:vaani/main.dart' show appLogger; + +class LibrarySwitchChip extends HookConsumerWidget { + const LibrarySwitchChip({super.key, required this.libraries}); + final List libraries; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final apiSettings = ref.watch(apiSettingsProvider); + + return ActionChip( + avatar: Icon( + AbsIcons.getIconByName( + apiSettings.activeLibraryId != null + ? libraries + .firstWhere((lib) => lib.id == apiSettings.activeLibraryId) + .icon + : libraries.first.icon, + ), + ), // Replace with your icon + label: const Text('Change Library'), + // Enable only if libraries are loaded and not empty + onPressed: libraries.isNotEmpty + ? () => showLibrarySwitcher(context, ref) + : null, // Disable if no libraries + ); + } +} + +// --- Helper Function to Show the Switcher --- +void showLibrarySwitcher(BuildContext context, WidgetRef ref) { + final content = _LibrarySelectionContent(); + + // --- Platform-Specific UI --- + bool isDesktop = false; + if (!kIsWeb) { + // dart:io Platform is not available on web + isDesktop = Platform.isLinux || Platform.isMacOS || Platform.isWindows; + } else { + // Basic web detection (might need refinement based on screen size) + // Consider using MediaQuery for a size-based check instead for web/tablet + final size = MediaQuery.of(context).size; + isDesktop = size.width > 600; // Example threshold for "desktop-like" layout + } + + if (isDesktop) { + // --- Desktop: Use AlertDialog --- + showDialog( + context: context, + builder: (dialogContext) => AlertDialog( + title: const Text('Select Library'), + content: SizedBox( + // Constrain size for dialogs + width: 300, // Adjust as needed + // Make content scrollable if list is long + child: Scrollbar(child: content), + ), + actions: [ + TextButton( + onPressed: () { + // Invalidate the provider to trigger a refetch + ref.invalidate(librariesProvider); + Navigator.pop(dialogContext); + }, + child: const Text('Refresh'), + ), + TextButton( + onPressed: () => Navigator.pop(dialogContext), + child: const Text('Cancel'), + ), + ], + ), + ); + } else { + // --- Mobile/Tablet: Use BottomSheet --- + showModalBottomSheet( + context: context, + // Make it scrollable and control height + isScrollControlled: true, + constraints: BoxConstraints( + maxHeight: + MediaQuery.of(context).size.height * 0.6, // Max 60% of screen + ), + builder: (sheetContext) => Padding( + // Add padding within the bottom sheet + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, // Take minimum necessary height + children: [ + const Text( + 'Select Library', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 10), + const Divider(), + Flexible( + // Allow the list to take remaining space and scroll + child: Scrollbar(child: content), + ), + const SizedBox(height: 10), + ElevatedButton.icon( + icon: const Icon(Icons.refresh), + label: const Text('Refresh'), + onPressed: () { + // Invalidate the provider to trigger a refetch + ref.invalidate(librariesProvider); + }, + ), + ], + ), + ), + ); + } +} + +// --- Widget for the Selection List Content (Reusable) --- +class _LibrarySelectionContent extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + final librariesAsyncValue = ref.watch(librariesProvider); + final currentLibraryId = ref.watch( + apiSettingsProvider.select((settings) => settings.activeLibraryId), + ); + final errorColor = Theme.of(context).colorScheme.error; + return librariesAsyncValue.when( + // --- Loading State --- + loading: () => const Center(child: CircularProgressIndicator()), + + // --- Error State --- + error: (error, stackTrace) => Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.error_outline, color: errorColor), + const SizedBox(height: 10), + Text( + 'Error loading libraries: $error', + textAlign: TextAlign.center, + style: TextStyle(color: errorColor), + ), + const SizedBox(height: 16), + ElevatedButton.icon( + icon: const Icon(Icons.refresh), + label: const Text('Retry'), + onPressed: () { + // Invalidate the provider to trigger a refetch + ref.invalidate(librariesProvider); + }, + ), + ], + ), + ), + ), + + // --- Data State --- + data: (libraries) { + // Handle case where data loaded successfully but is empty + if (libraries.isEmpty) { + return const Center( + child: Padding( + padding: EdgeInsets.all(16.0), + child: Text('No libraries available.'), + ), + ); + } + + // Build the list if libraries are available + return Scrollbar( + // Add scrollbar for potentially long lists + child: ListView.builder( + shrinkWrap: true, // Important for Dialog/BottomSheet sizing + itemCount: libraries.length, + itemBuilder: (context, index) { + final library = libraries[index]; + final bool isSelected = library.id == currentLibraryId; + + return ListTile( + title: Text(library.name), + leading: Icon(AbsIcons.getIconByName(library.icon)), + selected: isSelected, + trailing: isSelected ? const Icon(Icons.check) : null, + onTap: () { + appLogger.info( + 'Selected library: ${library.name} (ID: ${library.id})', + ); + // Get current settings state + final currentSettings = ref.read(apiSettingsProvider); + // Update the active library ID + ref + .read(apiSettingsProvider.notifier) + .updateState( + currentSettings.copyWith(activeLibraryId: library.id), + ); + // Close the dialog/bottom sheet + Navigator.pop(context); + }, + ); + }, + ), + ); + }, + ); + } +} diff --git a/lib/features/you/view/you_page.dart b/lib/features/you/view/you_page.dart index a171a70..fba9048 100644 --- a/lib/features/you/view/you_page.dart +++ b/lib/features/you/view/you_page.dart @@ -1,54 +1,42 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:vaani/api/api_provider.dart'; +import 'package:vaani/api/library_provider.dart' show librariesProvider; +import 'package:vaani/features/player/view/mini_player_bottom_padding.dart'; +import 'package:vaani/features/you/view/widgets/library_switch_chip.dart'; import 'package:vaani/router/router.dart'; +import 'package:vaani/settings/constants.dart'; import 'package:vaani/shared/utils.dart'; import 'package:vaani/shared/widgets/not_implemented.dart'; +import 'package:vaani/shared/widgets/vaani_logo.dart'; class YouPage extends HookConsumerWidget { - const YouPage({ - super.key, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final me = ref.watch(meProvider); - return me.when( - data: (data) { - return _YouPage(userData: data); - }, - loading: () => const CircularProgressIndicator(), - error: (error, stack) => Text('Error: $error'), - ); - } -} - -class _YouPage extends HookConsumerWidget { - const _YouPage({ - super.key, - required this.userData, - }); - - final User userData; + const YouPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final api = ref.watch(authenticatedApiProvider); + final librariesAsyncValue = ref.watch(librariesProvider); return Scaffold( appBar: AppBar( // title: const Text('You'), - backgroundColor: Colors.transparent, actions: [ + IconButton( + tooltip: 'Logs', + icon: const Icon(Icons.bug_report), + onPressed: () { + context.pushNamed(Routes.logs.name); + }, + ), // IconButton( // icon: const Icon(Icons.edit), // onPressed: () { // // Handle edit profile // }, // ), - // settings button IconButton( + tooltip: 'Settings', icon: const Icon(Icons.settings), onPressed: () { context.pushNamed(Routes.settings.name); @@ -64,30 +52,7 @@ class _YouPage extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - CircleAvatar( - radius: 40, - // backgroundImage: NetworkImage(userData.avatarUrl), - // first letter of the username - child: Text( - userData.username[0].toUpperCase(), - style: const TextStyle( - fontSize: 32, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox(width: 16), - Text( - userData.username, - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - ], - ), + UserBar(), const SizedBox(height: 16), Wrap( spacing: 8, @@ -99,7 +64,36 @@ class _YouPage extends HookConsumerWidget { context.pushNamed(Routes.userManagement.name); }, ), - // ActionChip( + librariesAsyncValue.when( + data: (libraries) => + LibrarySwitchChip(libraries: libraries), + loading: () => const ActionChip( + avatar: SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator(strokeWidth: 2), + ), + label: Text('Loading Libs...'), + onPressed: null, // Disable while loading + ), + error: (error, stack) => ActionChip( + avatar: Icon( + Icons.error_outline, + color: Theme.of(context).colorScheme.error, + ), + label: const Text('Error Loading Libs'), + onPressed: () { + // Maybe show error details or allow retry + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Failed to load libraries: $error', + ), + ), + ); + }, + ), + ), // ActionChip( // avatar: const Icon(Icons.logout), // label: const Text('Logout'), // onPressed: () { @@ -121,21 +115,6 @@ class _YouPage extends HookConsumerWidget { title: const Text('My Playlists'), onTap: () { // Handle navigation to playlists - }, - ), - ListTile( - leading: const Icon(Icons.help), - title: const Text('Help'), - onTap: () { - // Handle navigation to help website - showNotImplementedToast(context); - }, - ), - ListTile( - leading: const Icon(Icons.info), - title: const Text('About'), - onTap: () { - // Handle navigation to about showNotImplementedToast(context); }, ), @@ -149,16 +128,102 @@ class _YouPage extends HookConsumerWidget { ); }, ), - // const SizedBox(height: 16), - // const Text('App Version: 1.0.0'), - // const Text('Server Version: 1.0.0'), - // const Text('Author: Your Name'), + ListTile( + leading: const Icon(Icons.help), + title: const Text('Help'), + onTap: () { + // Handle navigation to help website + showNotImplementedToast(context); + }, + ), + AboutListTile( + icon: const Icon(Icons.info), + applicationName: AppMetadata.appName, + applicationVersion: AppMetadata.version, + applicationLegalese: + 'Made with ❤️ by ${AppMetadata.author}', + aboutBoxChildren: [ + // link to github repo + ListTile( + leading: Icon(Icons.code), + title: Text('Source Code'), + onTap: () { + handleLaunchUrl(AppMetadata.githubRepo); + }, + ), + ], + // apply blend mode to the icon to match the primary color + applicationIcon: ColorFiltered( + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.primary, + BlendMode.srcIn, + ), + child: const VaaniLogo(size: 48), + ), + ), ], ), ), ), + SliverToBoxAdapter(child: MiniPlayerBottomPadding()), ], ), ); } } + +class UserBar extends HookConsumerWidget { + const UserBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final me = ref.watch(meProvider); + final api = ref.watch(authenticatedApiProvider); + + final themeData = Theme.of(context); + final textTheme = themeData.textTheme; + + return me.when( + data: (userData) { + return Row( + children: [ + CircleAvatar( + radius: 40, + // backgroundImage: NetworkImage(userData.avatarUrl), + // first letter of the username + child: Text( + userData.username[0].toUpperCase(), + style: textTheme.headlineLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(width: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + userData.username, + style: textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + api.baseUrl.toString(), + style: textTheme.bodyMedium?.copyWith( + color: themeData.colorScheme.onSurface.withValues( + alpha: 0.6, + ), + ), + ), + ], + ), + ], + ); + }, + loading: () => const CircularProgressIndicator(), + error: (error, stack) => Text('Error: $error'), + ); + } +} diff --git a/lib/hacks/fix_autofill_losing_focus.dart b/lib/hacks/fix_autofill_losing_focus.dart index ffa7da6..3763654 100644 --- a/lib/hacks/fix_autofill_losing_focus.dart +++ b/lib/hacks/fix_autofill_losing_focus.dart @@ -14,10 +14,7 @@ import 'package:flutter/material.dart'; class InactiveFocusScopeObserver extends StatefulWidget { final Widget child; - const InactiveFocusScopeObserver({ - super.key, - required this.child, - }); + const InactiveFocusScopeObserver({super.key, required this.child}); @override State createState() => @@ -39,10 +36,8 @@ class _InactiveFocusScopeObserverState } @override - Widget build(BuildContext context) => FocusScope( - node: _focusScope, - child: widget.child, - ); + Widget build(BuildContext context) => + FocusScope(node: _focusScope, child: widget.child); @override void dispose() { diff --git a/lib/main.dart b/lib/main.dart index 8eef350..0eb4cc0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,18 +1,22 @@ +import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; import 'package:vaani/api/server_provider.dart'; import 'package:vaani/db/storage.dart'; import 'package:vaani/features/downloads/providers/download_manager.dart'; +import 'package:vaani/features/logging/core/logger.dart'; import 'package:vaani/features/playback_reporting/providers/playback_reporter_provider.dart'; import 'package:vaani/features/player/core/init.dart'; -import 'package:vaani/features/player/providers/audiobook_player.dart'; +import 'package:vaani/features/player/providers/audiobook_player.dart' + show audiobookPlayerProvider, simpleAudiobookPlayerProvider; import 'package:vaani/features/shake_detection/providers/shake_detector.dart'; import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart'; import 'package:vaani/router/router.dart'; import 'package:vaani/settings/api_settings_provider.dart'; import 'package:vaani/settings/app_settings_provider.dart'; -import 'package:vaani/shared/extensions/duration_format.dart'; +import 'package:vaani/theme/providers/system_theme_provider.dart'; +import 'package:vaani/theme/providers/theme_from_cover_provider.dart'; import 'package:vaani/theme/theme.dart'; final appLogger = Logger('vaani'); @@ -20,13 +24,7 @@ final appLogger = Logger('vaani'); void main() async { WidgetsFlutterBinding.ensureInitialized(); // Configure the root Logger - Logger.root.level = Level.FINE; // Capture all logs - Logger.root.onRecord.listen((record) { - // Print log records to the console - debugPrint( - '${record.loggerName}: ${record.level.name}: ${record.time.time}: ${record.message}', - ); - }); + await initLogging(); // initialize the storage await initStorage(); @@ -34,13 +32,8 @@ void main() async { // initialize audio player await configurePlayer(); - // run the app - runApp( - const ProviderScope( - child: _EagerInitialization(child: MyApp()), - ), - ); + runApp(const ProviderScope(child: _EagerInitialization(child: MyApp()))); } var routerConfig = const MyAppRouter().config; @@ -58,19 +51,85 @@ class MyApp extends ConsumerWidget { if (needOnboarding) { routerConfig.goNamed(Routes.onboarding.name); } + final appSettings = ref.watch(appSettingsProvider); + final themeSettings = appSettings.themeSettings; + ColorScheme lightColorScheme = brandLightColorScheme; + ColorScheme darkColorScheme = brandDarkColorScheme; + + final shouldUseHighContrast = + themeSettings.highContrast || MediaQuery.of(context).highContrast; + + if (shouldUseHighContrast) { + lightColorScheme = lightColorScheme.copyWith(surface: Colors.white); + darkColorScheme = darkColorScheme.copyWith(surface: Colors.black); + } + + if (themeSettings.useMaterialThemeFromSystem) { + var themes = ref.watch( + systemThemeProvider(highContrast: shouldUseHighContrast), + ); + if (themes.value != null) { + lightColorScheme = themes.value!.$1; + darkColorScheme = themes.value!.$2; + } + } + + if (themeSettings.useCurrentPlayerThemeThroughoutApp) { + try { + final player = ref.watch(audiobookPlayerProvider); + if (player.book != null) { + final themeLight = ref.watch( + themeOfLibraryItemProvider( + player.book!.libraryItemId, + highContrast: shouldUseHighContrast, + brightness: Brightness.light, + ), + ); + final themeDark = ref.watch( + themeOfLibraryItemProvider( + player.book!.libraryItemId, + highContrast: shouldUseHighContrast, + brightness: Brightness.dark, + ), + ); + if (themeLight.value != null && themeDark.value != null) { + lightColorScheme = themeLight.value!; + darkColorScheme = themeDark.value!; + } + } + } catch (e) { + debugPrintStack(stackTrace: StackTrace.current, label: e.toString()); + appLogger.severe('not building with player theme'); + appLogger.severe(e.toString()); + } + } + final appThemeLight = ThemeData( + useMaterial3: true, + colorScheme: lightColorScheme.harmonized(), + ); + final appThemeDark = ThemeData( + useMaterial3: true, + colorScheme: darkColorScheme.harmonized(), + brightness: Brightness.dark, + // TODO bottom sheet theme is not working + bottomSheetTheme: BottomSheetThemeData( + backgroundColor: darkColorScheme.surface, + ), + ); try { return MaterialApp.router( // debugShowCheckedModeBanner: false, - theme: lightTheme, - darkTheme: darkTheme, - themeMode: ref.watch(appSettingsProvider).themeSettings.isDarkMode - ? ThemeMode.dark - : ThemeMode.light, + theme: appThemeLight, + darkTheme: appThemeDark, + themeMode: themeSettings.themeMode, routerConfig: routerConfig, + themeAnimationCurve: Curves.easeInOut, ); } catch (e) { debugPrintStack(stackTrace: StackTrace.current, label: e.toString()); + appLogger.severe(e.toString()); + if (needOnboarding) { routerConfig.goNamed(Routes.onboarding.name); } @@ -97,6 +156,7 @@ class _EagerInitialization extends ConsumerWidget { ref.watch(shakeDetectorProvider); } catch (e) { debugPrintStack(stackTrace: StackTrace.current, label: e.toString()); + appLogger.severe(e.toString()); } return child; diff --git a/lib/models/error_response.dart b/lib/models/error_response.dart index 2e4c926..13352be 100644 --- a/lib/models/error_response.dart +++ b/lib/models/error_response.dart @@ -1,19 +1,24 @@ import 'package:http/http.dart' as http; import 'package:logging/logging.dart'; +import 'package:vaani/shared/extensions/obfuscation.dart'; final _logger = Logger('ErrorResponse'); class ErrorResponseHandler { String? name; http.Response _response; + bool logRawResponse; ErrorResponseHandler({ this.name, http.Response? response, + this.logRawResponse = false, }) : _response = response ?? http.Response('', 418); void storeError(http.Response response, [Object? error]) { - _logger.fine('for $name got response: $response'); + if (logRawResponse) { + _logger.fine('for $name got response: ${response.obfuscate()}'); + } _response = response; } diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 5b3ab41..202ee79 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -3,8 +3,11 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:vaani/api/api_provider.dart'; +import 'package:vaani/main.dart'; import 'package:vaani/router/router.dart'; import 'package:vaani/settings/api_settings_provider.dart'; +import 'package:vaani/settings/app_settings_provider.dart' + show appSettingsProvider; import '../shared/widgets/shelves/home_shelf.dart'; @@ -16,9 +19,10 @@ class HomePage extends HookConsumerWidget { final views = ref.watch(personalizedViewProvider); final apiSettings = ref.watch(apiSettingsProvider); final scrollController = useScrollController(); + final appSettings = ref.watch(appSettingsProvider); + final homePageSettings = appSettings.homePageSettings; return Scaffold( appBar: AppBar( - backgroundColor: Colors.transparent, title: GestureDetector( child: Text( 'Vaani', @@ -48,6 +52,11 @@ class HomePage extends HookConsumerWidget { // try again button ElevatedButton( onPressed: () { + ref + .read(apiSettingsProvider.notifier) + .updateState( + apiSettings.copyWith(activeLibraryId: null), + ); ref.invalidate(personalizedViewProvider); }, child: const Text('Try again'), @@ -59,12 +68,25 @@ class HomePage extends HookConsumerWidget { final shelvesToDisplay = data // .where((element) => !element.id.contains('discover')) .map((shelf) { - debugPrint('building shelf ${shelf.label}'); - return HomeShelf( - title: shelf.label, - shelf: shelf, - ); - }).toList(); + appLogger.fine('building shelf ${shelf.label}'); + // check if showPlayButton is enabled for the shelf + // using the id of the shelf + final showPlayButton = switch (shelf.id) { + 'continue-listening' => + homePageSettings.showPlayButtonOnContinueListeningShelf, + 'continue-series' => + homePageSettings.showPlayButtonOnContinueSeriesShelf, + 'listen-again' => + homePageSettings.showPlayButtonOnListenAgainShelf, + _ => homePageSettings.showPlayButtonOnAllRemainingShelves, + }; + return HomeShelf( + title: shelf.label, + shelf: shelf, + showPlayButton: showPlayButton, + ); + }) + .toList(); return RefreshIndicator( onRefresh: () async { return ref.refresh(personalizedViewProvider); @@ -72,7 +94,7 @@ class HomePage extends HookConsumerWidget { child: ListView.separated( itemBuilder: (context, index) => shelvesToDisplay[index], separatorBuilder: (context, index) => Divider( - color: Theme.of(context).dividerColor.withOpacity(0.1), + color: Theme.of(context).dividerColor.withValues(alpha: 0.1), indent: 16, endIndent: 16, ), @@ -113,10 +135,6 @@ class HomePageSkeleton extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold( - body: Center( - child: CircularProgressIndicator(), - ), - ); + return const Scaffold(body: Center(child: CircularProgressIndicator())); } } diff --git a/lib/pages/library_page.dart b/lib/pages/library_page.dart index fb97221..d203987 100644 --- a/lib/pages/library_page.dart +++ b/lib/pages/library_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:vaani/api/api_provider.dart'; +import 'package:vaani/main.dart'; import 'package:vaani/settings/api_settings_provider.dart'; import '../shared/widgets/drawer.dart'; @@ -16,7 +17,9 @@ class LibraryPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { // set the library id as the active library if (libraryId != null) { - ref.read(apiSettingsProvider.notifier).updateState( + ref + .read(apiSettingsProvider.notifier) + .updateState( ref.watch(apiSettingsProvider).copyWith(activeLibraryId: libraryId), ); } @@ -47,12 +50,10 @@ class LibraryPage extends HookConsumerWidget { final shelvesToDisplay = data // .where((element) => !element.id.contains('discover')) .map((shelf) { - debugPrint('building shelf ${shelf.label}'); - return HomeShelf( - title: shelf.label, - shelf: shelf, - ); - }).toList(); + appLogger.fine('building shelf ${shelf.label}'); + return HomeShelf(title: shelf.label, shelf: shelf); + }) + .toList(); return RefreshIndicator( onRefresh: () async { return ref.refresh(personalizedViewProvider); @@ -60,7 +61,7 @@ class LibraryPage extends HookConsumerWidget { child: ListView.separated( itemBuilder: (context, index) => shelvesToDisplay[index], separatorBuilder: (context, index) => Divider( - color: Theme.of(context).dividerColor.withOpacity(0.1), + color: Theme.of(context).dividerColor.withValues(alpha: 0.1), indent: 16, endIndent: 16, ), @@ -84,10 +85,6 @@ class LibraryPageSkeleton extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold( - body: Center( - child: CircularProgressIndicator(), - ), - ); + return const Scaffold(body: Center(child: CircularProgressIndicator())); } } diff --git a/lib/router/constants.dart b/lib/router/constants.dart index 4d8e7c1..3a556bf 100644 --- a/lib/router/constants.dart +++ b/lib/router/constants.dart @@ -3,14 +3,8 @@ part of 'router.dart'; class Routes { - static const home = _SimpleRoute( - pathName: '', - name: 'home', - ); - static const onboarding = _SimpleRoute( - pathName: 'login', - name: 'onboarding', - ); + static const home = _SimpleRoute(pathName: '', name: 'home'); + static const onboarding = _SimpleRoute(pathName: 'login', name: 'onboarding'); static const library = _SimpleRoute( pathName: 'library', pathParamName: 'libraryId', @@ -23,9 +17,11 @@ class Routes { ); // Local settings - static const settings = _SimpleRoute( - pathName: 'config', - name: 'settings', + static const settings = _SimpleRoute(pathName: 'config', name: 'settings'); + static const themeSettings = _SimpleRoute( + pathName: 'theme', + name: 'themeSettings', + parentRoute: settings, ); static const autoSleepTimerSettings = _SimpleRoute( pathName: 'autoSleepTimer', @@ -47,6 +43,11 @@ class Routes { name: 'shakeDetectorSettings', parentRoute: settings, ); + static const homePageSettings = _SimpleRoute( + pathName: 'homePage', + name: 'homePageSettings', + parentRoute: settings, + ); // search and explore static const search = _SimpleRoute( @@ -54,10 +55,7 @@ class Routes { name: 'search', // parentRoute: library, ); - static const explore = _SimpleRoute( - pathName: 'explore', - name: 'explore', - ); + static const explore = _SimpleRoute(pathName: 'explore', name: 'explore'); // downloads static const downloads = _SimpleRoute( @@ -73,10 +71,7 @@ class Routes { ); // you page for the user - static const you = _SimpleRoute( - pathName: 'you', - name: 'you', - ); + static const you = _SimpleRoute(pathName: 'you', name: 'you'); // user management static const userManagement = _SimpleRoute( @@ -90,6 +85,9 @@ class Routes { name: 'openIDCallback', parentRoute: onboarding, ); + + // logs page + static const logs = _SimpleRoute(pathName: 'logs', name: 'logs'); } // a class to store path diff --git a/lib/router/models/library_item_extras.dart b/lib/router/models/library_item_extras.dart index 0d50040..e67a508 100644 --- a/lib/router/models/library_item_extras.dart +++ b/lib/router/models/library_item_extras.dart @@ -11,10 +11,9 @@ part 'library_item_extras.freezed.dart'; /// [book] is the book that the item represents /// [heroTagSuffix] is the suffix to use for the hero tag to avoid conflicts @freezed -class LibraryItemExtras with _$LibraryItemExtras { +sealed class LibraryItemExtras with _$LibraryItemExtras { const factory LibraryItemExtras({ BookMinified? book, @Default('') String heroTagSuffix, }) = _LibraryItemExtras; - } diff --git a/lib/router/models/library_item_extras.freezed.dart b/lib/router/models/library_item_extras.freezed.dart index 900fdec..fe6ae17 100644 --- a/lib/router/models/library_item_extras.freezed.dart +++ b/lib/router/models/library_item_extras.freezed.dart @@ -1,5 +1,5 @@ -// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark @@ -9,156 +9,260 @@ part of 'library_item_extras.dart'; // FreezedGenerator // ************************************************************************** +// dart format off T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - /// @nodoc mixin _$LibraryItemExtras { - BookMinified? get book => throw _privateConstructorUsedError; - String get heroTagSuffix => throw _privateConstructorUsedError; - /// Create a copy of LibraryItemExtras - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $LibraryItemExtrasCopyWith get copyWith => - throw _privateConstructorUsedError; + BookMinified? get book; String get heroTagSuffix; +/// Create a copy of LibraryItemExtras +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$LibraryItemExtrasCopyWith get copyWith => _$LibraryItemExtrasCopyWithImpl(this as LibraryItemExtras, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is LibraryItemExtras&&(identical(other.book, book) || other.book == book)&&(identical(other.heroTagSuffix, heroTagSuffix) || other.heroTagSuffix == heroTagSuffix)); +} + + +@override +int get hashCode => Object.hash(runtimeType,book,heroTagSuffix); + +@override +String toString() { + return 'LibraryItemExtras(book: $book, heroTagSuffix: $heroTagSuffix)'; +} + + } /// @nodoc -abstract class $LibraryItemExtrasCopyWith<$Res> { - factory $LibraryItemExtrasCopyWith( - LibraryItemExtras value, $Res Function(LibraryItemExtras) then) = - _$LibraryItemExtrasCopyWithImpl<$Res, LibraryItemExtras>; - @useResult - $Res call({BookMinified? book, String heroTagSuffix}); -} +abstract mixin class $LibraryItemExtrasCopyWith<$Res> { + factory $LibraryItemExtrasCopyWith(LibraryItemExtras value, $Res Function(LibraryItemExtras) _then) = _$LibraryItemExtrasCopyWithImpl; +@useResult +$Res call({ + BookMinified? book, String heroTagSuffix +}); + + + +} /// @nodoc -class _$LibraryItemExtrasCopyWithImpl<$Res, $Val extends LibraryItemExtras> +class _$LibraryItemExtrasCopyWithImpl<$Res> implements $LibraryItemExtrasCopyWith<$Res> { - _$LibraryItemExtrasCopyWithImpl(this._value, this._then); + _$LibraryItemExtrasCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final LibraryItemExtras _self; + final $Res Function(LibraryItemExtras) _then; - /// Create a copy of LibraryItemExtras - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? book = freezed, - Object? heroTagSuffix = null, - }) { - return _then(_value.copyWith( - book: freezed == book - ? _value.book - : book // ignore: cast_nullable_to_non_nullable - as BookMinified?, - heroTagSuffix: null == heroTagSuffix - ? _value.heroTagSuffix - : heroTagSuffix // ignore: cast_nullable_to_non_nullable - as String, - ) as $Val); - } +/// Create a copy of LibraryItemExtras +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? book = freezed,Object? heroTagSuffix = null,}) { + return _then(_self.copyWith( +book: freezed == book ? _self.book : book // ignore: cast_nullable_to_non_nullable +as BookMinified?,heroTagSuffix: null == heroTagSuffix ? _self.heroTagSuffix : heroTagSuffix // ignore: cast_nullable_to_non_nullable +as String, + )); } -/// @nodoc -abstract class _$$LibraryItemExtrasImplCopyWith<$Res> - implements $LibraryItemExtrasCopyWith<$Res> { - factory _$$LibraryItemExtrasImplCopyWith(_$LibraryItemExtrasImpl value, - $Res Function(_$LibraryItemExtrasImpl) then) = - __$$LibraryItemExtrasImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({BookMinified? book, String heroTagSuffix}); } -/// @nodoc -class __$$LibraryItemExtrasImplCopyWithImpl<$Res> - extends _$LibraryItemExtrasCopyWithImpl<$Res, _$LibraryItemExtrasImpl> - implements _$$LibraryItemExtrasImplCopyWith<$Res> { - __$$LibraryItemExtrasImplCopyWithImpl(_$LibraryItemExtrasImpl _value, - $Res Function(_$LibraryItemExtrasImpl) _then) - : super(_value, _then); - /// Create a copy of LibraryItemExtras - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? book = freezed, - Object? heroTagSuffix = null, - }) { - return _then(_$LibraryItemExtrasImpl( - book: freezed == book - ? _value.book - : book // ignore: cast_nullable_to_non_nullable - as BookMinified?, - heroTagSuffix: null == heroTagSuffix - ? _value.heroTagSuffix - : heroTagSuffix // ignore: cast_nullable_to_non_nullable - as String, - )); - } +/// Adds pattern-matching-related methods to [LibraryItemExtras]. +extension LibraryItemExtrasPatterns on LibraryItemExtras { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _LibraryItemExtras value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _LibraryItemExtras() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _LibraryItemExtras value) $default,){ +final _that = this; +switch (_that) { +case _LibraryItemExtras(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _LibraryItemExtras value)? $default,){ +final _that = this; +switch (_that) { +case _LibraryItemExtras() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( BookMinified? book, String heroTagSuffix)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _LibraryItemExtras() when $default != null: +return $default(_that.book,_that.heroTagSuffix);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( BookMinified? book, String heroTagSuffix) $default,) {final _that = this; +switch (_that) { +case _LibraryItemExtras(): +return $default(_that.book,_that.heroTagSuffix);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( BookMinified? book, String heroTagSuffix)? $default,) {final _that = this; +switch (_that) { +case _LibraryItemExtras() when $default != null: +return $default(_that.book,_that.heroTagSuffix);case _: + return null; + +} +} + } /// @nodoc -class _$LibraryItemExtrasImpl implements _LibraryItemExtras { - const _$LibraryItemExtrasImpl({this.book, this.heroTagSuffix = ''}); - @override - final BookMinified? book; - @override - @JsonKey() - final String heroTagSuffix; +class _LibraryItemExtras implements LibraryItemExtras { + const _LibraryItemExtras({this.book, this.heroTagSuffix = ''}); + - @override - String toString() { - return 'LibraryItemExtras(book: $book, heroTagSuffix: $heroTagSuffix)'; - } +@override final BookMinified? book; +@override@JsonKey() final String heroTagSuffix; - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$LibraryItemExtrasImpl && - (identical(other.book, book) || other.book == book) && - (identical(other.heroTagSuffix, heroTagSuffix) || - other.heroTagSuffix == heroTagSuffix)); - } +/// Create a copy of LibraryItemExtras +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$LibraryItemExtrasCopyWith<_LibraryItemExtras> get copyWith => __$LibraryItemExtrasCopyWithImpl<_LibraryItemExtras>(this, _$identity); - @override - int get hashCode => Object.hash(runtimeType, book, heroTagSuffix); - /// Create a copy of LibraryItemExtras - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$LibraryItemExtrasImplCopyWith<_$LibraryItemExtrasImpl> get copyWith => - __$$LibraryItemExtrasImplCopyWithImpl<_$LibraryItemExtrasImpl>( - this, _$identity); + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _LibraryItemExtras&&(identical(other.book, book) || other.book == book)&&(identical(other.heroTagSuffix, heroTagSuffix) || other.heroTagSuffix == heroTagSuffix)); } -abstract class _LibraryItemExtras implements LibraryItemExtras { - const factory _LibraryItemExtras( - {final BookMinified? book, - final String heroTagSuffix}) = _$LibraryItemExtrasImpl; - @override - BookMinified? get book; - @override - String get heroTagSuffix; +@override +int get hashCode => Object.hash(runtimeType,book,heroTagSuffix); - /// Create a copy of LibraryItemExtras - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$LibraryItemExtrasImplCopyWith<_$LibraryItemExtrasImpl> get copyWith => - throw _privateConstructorUsedError; +@override +String toString() { + return 'LibraryItemExtras(book: $book, heroTagSuffix: $heroTagSuffix)'; } + + +} + +/// @nodoc +abstract mixin class _$LibraryItemExtrasCopyWith<$Res> implements $LibraryItemExtrasCopyWith<$Res> { + factory _$LibraryItemExtrasCopyWith(_LibraryItemExtras value, $Res Function(_LibraryItemExtras) _then) = __$LibraryItemExtrasCopyWithImpl; +@override @useResult +$Res call({ + BookMinified? book, String heroTagSuffix +}); + + + + +} +/// @nodoc +class __$LibraryItemExtrasCopyWithImpl<$Res> + implements _$LibraryItemExtrasCopyWith<$Res> { + __$LibraryItemExtrasCopyWithImpl(this._self, this._then); + + final _LibraryItemExtras _self; + final $Res Function(_LibraryItemExtras) _then; + +/// Create a copy of LibraryItemExtras +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? book = freezed,Object? heroTagSuffix = null,}) { + return _then(_LibraryItemExtras( +book: freezed == book ? _self.book : book // ignore: cast_nullable_to_non_nullable +as BookMinified?,heroTagSuffix: null == heroTagSuffix ? _self.heroTagSuffix : heroTagSuffix // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/lib/router/router.dart b/lib/router/router.dart index 8910fc3..949d0ee 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -5,24 +5,29 @@ import 'package:vaani/features/explore/view/explore_page.dart'; import 'package:vaani/features/explore/view/search_result_page.dart'; import 'package:vaani/features/item_viewer/view/library_item_page.dart'; import 'package:vaani/features/library_browser/view/library_browser_page.dart'; +import 'package:vaani/features/logging/view/logs_page.dart'; import 'package:vaani/features/onboarding/view/callback_page.dart'; import 'package:vaani/features/onboarding/view/onboarding_single_page.dart'; import 'package:vaani/features/you/view/server_manager.dart'; import 'package:vaani/features/you/view/you_page.dart'; +import 'package:vaani/main.dart'; import 'package:vaani/pages/home_page.dart'; import 'package:vaani/settings/view/app_settings_page.dart'; import 'package:vaani/settings/view/auto_sleep_timer_settings_page.dart'; import 'package:vaani/settings/view/notification_settings_page.dart'; import 'package:vaani/settings/view/player_settings_page.dart'; import 'package:vaani/settings/view/shake_detector_settings_page.dart'; +import 'package:vaani/settings/view/theme_settings_page.dart'; +import 'package:vaani/settings/view/home_page_settings_page.dart'; import 'scaffold_with_nav_bar.dart'; import 'transitions/slide.dart'; part 'constants.dart'; -final GlobalKey rootNavigatorKey = - GlobalKey(debugLabel: 'root'); +final GlobalKey rootNavigatorKey = GlobalKey( + debugLabel: 'root', +); final GlobalKey sectionHomeNavigatorKey = GlobalKey(debugLabel: 'HomeNavigator'); @@ -31,34 +36,35 @@ class MyAppRouter { const MyAppRouter(); GoRouter get config => GoRouter( - initialLocation: Routes.home.localPath, - debugLogDiagnostics: true, + initialLocation: Routes.home.localPath, + debugLogDiagnostics: true, + routes: [ + // sign in page + GoRoute( + path: Routes.onboarding.localPath, + name: Routes.onboarding.name, + builder: (context, state) => const OnboardingSinglePage(), routes: [ - // sign in page + // open id callback GoRoute( - path: Routes.onboarding.localPath, - name: Routes.onboarding.name, - builder: (context, state) => const OnboardingSinglePage(), - routes: [ - // open id callback - GoRoute( - path: Routes.openIDCallback.pathName, - name: Routes.openIDCallback.name, - pageBuilder: handleCallback, - ), - ], - ), - // callback for open id - // need to duplicate because of https://github.com/flutter/flutter/issues/100624 - GoRoute( - path: Routes.openIDCallback.localPath, - // name: Routes.openIDCallback.name, - // builder: handleCallback, + path: Routes.openIDCallback.pathName, + name: Routes.openIDCallback.name, pageBuilder: handleCallback, ), - // The main app shell - StatefulShellRoute.indexedStack( - builder: ( + ], + ), + // callback for open id + // need to duplicate because of https://github.com/flutter/flutter/issues/100624 + GoRoute( + path: Routes.openIDCallback.localPath, + // name: Routes.openIDCallback.name, + // builder: handleCallback, + pageBuilder: handleCallback, + ), + // The main app shell + StatefulShellRoute.indexedStack( + builder: + ( BuildContext context, GoRouterState state, StatefulNavigationShell navigationShell, @@ -69,166 +75,187 @@ class MyAppRouter { // branches in a stateful way. return ScaffoldWithNavBar(navigationShell: navigationShell); }, - branches: [ - // The route branch for the first tab of the bottom navigation bar. - StatefulShellBranch( - navigatorKey: sectionHomeNavigatorKey, - routes: [ - GoRoute( - path: Routes.home.localPath, - name: Routes.home.name, - // builder: (context, state) => const HomePage(), - pageBuilder: defaultPageBuilder(const HomePage()), - ), - GoRoute( - path: Routes.libraryItem.localPath, - name: Routes.libraryItem.name, - // builder: (context, state) { - // final itemId = state - // .pathParameters[Routes.libraryItem.pathParamName]!; - // return LibraryItemPage( - // itemId: itemId, extra: state.extra); - // }, - pageBuilder: (context, state) { - final itemId = state - .pathParameters[Routes.libraryItem.pathParamName]!; - final child = - LibraryItemPage(itemId: itemId, extra: state.extra); - return buildPageWithDefaultTransition( - context: context, - state: state, - child: child, - ); - }, - ), - // downloads page - GoRoute( - path: Routes.downloads.localPath, - name: Routes.downloads.name, - pageBuilder: defaultPageBuilder(const DownloadsPage()), - ), - ], + branches: [ + // The route branch for the first tab of the bottom navigation bar. + StatefulShellBranch( + navigatorKey: sectionHomeNavigatorKey, + routes: [ + GoRoute( + path: Routes.home.localPath, + name: Routes.home.name, + // builder: (context, state) => const HomePage(), + pageBuilder: defaultPageBuilder(const HomePage()), ), + GoRoute( + path: Routes.libraryItem.localPath, + name: Routes.libraryItem.name, + // builder: (context, state) { + // final itemId = state + // .pathParameters[Routes.libraryItem.pathParamName]!; + // return LibraryItemPage( + // itemId: itemId, extra: state.extra); + // }, + pageBuilder: (context, state) { + final itemId = + state.pathParameters[Routes.libraryItem.pathParamName]!; + final child = LibraryItemPage( + itemId: itemId, + extra: state.extra, + ); + return buildPageWithDefaultTransition( + context: context, + state: state, + child: child, + ); + }, + ), + // downloads page + GoRoute( + path: Routes.downloads.localPath, + name: Routes.downloads.name, + pageBuilder: defaultPageBuilder(const DownloadsPage()), + ), + ], + ), - // Library page - StatefulShellBranch( - routes: [ + // Library page + StatefulShellBranch( + routes: [ + GoRoute( + path: Routes.libraryBrowser.localPath, + name: Routes.libraryBrowser.name, + pageBuilder: defaultPageBuilder(const LibraryBrowserPage()), + ), + ], + ), + // search/explore page + StatefulShellBranch( + routes: [ + GoRoute( + path: Routes.explore.localPath, + name: Routes.explore.name, + // builder: (context, state) => const ExplorePage(), + pageBuilder: defaultPageBuilder(const ExplorePage()), + ), + // search page + GoRoute( + path: Routes.search.localPath, + name: Routes.search.name, + // builder: (context, state) { + // final libraryId = state + // .pathParameters[Routes.library.pathParamName]!; + // return LibrarySearchPage( + // libraryId: libraryId, + // extra: state.extra, + // ); + // }, + pageBuilder: (context, state) { + final queryParam = state.uri.queryParameters['q']!; + final category = state.uri.queryParameters['category']; + final child = SearchResultPage( + extra: state.extra, + query: queryParam, + category: category != null + ? SearchResultCategory.values.firstWhere( + (e) => e.toString().split('.').last == category, + ) + : null, + ); + return buildPageWithDefaultTransition( + context: context, + state: state, + child: child, + ); + }, + ), + ], + ), + // you page + StatefulShellBranch( + routes: [ + GoRoute( + path: Routes.you.localPath, + name: Routes.you.name, + pageBuilder: defaultPageBuilder(const YouPage()), + ), + GoRoute( + path: Routes.settings.localPath, + name: Routes.settings.name, + // builder: (context, state) => const AppSettingsPage(), + pageBuilder: defaultPageBuilder(const AppSettingsPage()), + routes: [ GoRoute( - path: Routes.libraryBrowser.localPath, - name: Routes.libraryBrowser.name, - pageBuilder: defaultPageBuilder(const LibraryBrowserPage()), + path: Routes.themeSettings.pathName, + name: Routes.themeSettings.name, + pageBuilder: defaultPageBuilder(const ThemeSettingsPage()), + ), + GoRoute( + path: Routes.autoSleepTimerSettings.pathName, + name: Routes.autoSleepTimerSettings.name, + pageBuilder: defaultPageBuilder( + const AutoSleepTimerSettingsPage(), + ), + ), + GoRoute( + path: Routes.notificationSettings.pathName, + name: Routes.notificationSettings.name, + pageBuilder: defaultPageBuilder( + const NotificationSettingsPage(), + ), + ), + GoRoute( + path: Routes.playerSettings.pathName, + name: Routes.playerSettings.name, + pageBuilder: defaultPageBuilder(const PlayerSettingsPage()), + ), + GoRoute( + path: Routes.shakeDetectorSettings.pathName, + name: Routes.shakeDetectorSettings.name, + pageBuilder: defaultPageBuilder( + const ShakeDetectorSettingsPage(), + ), + ), + GoRoute( + path: Routes.homePageSettings.pathName, + name: Routes.homePageSettings.name, + pageBuilder: defaultPageBuilder( + const HomePageSettingsPage(), + ), ), ], ), - // search/explore page - StatefulShellBranch( - routes: [ - GoRoute( - path: Routes.explore.localPath, - name: Routes.explore.name, - // builder: (context, state) => const ExplorePage(), - pageBuilder: defaultPageBuilder(const ExplorePage()), - ), - // search page - GoRoute( - path: Routes.search.localPath, - name: Routes.search.name, - // builder: (context, state) { - // final libraryId = state - // .pathParameters[Routes.library.pathParamName]!; - // return LibrarySearchPage( - // libraryId: libraryId, - // extra: state.extra, - // ); - // }, - pageBuilder: (context, state) { - final queryParam = state.uri.queryParameters['q']!; - final category = state.uri.queryParameters['category']; - final child = SearchResultPage( - extra: state.extra, - query: queryParam, - category: category != null - ? SearchResultCategory.values.firstWhere( - (e) => e.toString().split('.').last == category, - ) - : null, - ); - return buildPageWithDefaultTransition( - context: context, - state: state, - child: child, - ); - }, - ), - ], - ), - // you page - StatefulShellBranch( - routes: [ - GoRoute( - path: Routes.you.localPath, - name: Routes.you.name, - pageBuilder: defaultPageBuilder(const YouPage()), - ), - GoRoute( - path: Routes.settings.localPath, - name: Routes.settings.name, - // builder: (context, state) => const AppSettingsPage(), - pageBuilder: defaultPageBuilder(const AppSettingsPage()), - routes: [ - GoRoute( - path: Routes.autoSleepTimerSettings.pathName, - name: Routes.autoSleepTimerSettings.name, - pageBuilder: defaultPageBuilder( - const AutoSleepTimerSettingsPage(), - ), - ), - GoRoute( - path: Routes.notificationSettings.pathName, - name: Routes.notificationSettings.name, - pageBuilder: defaultPageBuilder( - const NotificationSettingsPage(), - ), - ), - GoRoute( - path: Routes.playerSettings.pathName, - name: Routes.playerSettings.name, - pageBuilder: - defaultPageBuilder(const PlayerSettingsPage()), - ), - GoRoute( - path: Routes.shakeDetectorSettings.pathName, - name: Routes.shakeDetectorSettings.name, - pageBuilder: defaultPageBuilder( - const ShakeDetectorSettingsPage(), - ), - ), - ], - ), - GoRoute( - path: Routes.userManagement.localPath, - name: Routes.userManagement.name, - // builder: (context, state) => const UserManagementPage(), - pageBuilder: defaultPageBuilder(const ServerManagerPage()), - ), - ], + GoRoute( + path: Routes.userManagement.localPath, + name: Routes.userManagement.name, + // builder: (context, state) => const UserManagementPage(), + pageBuilder: defaultPageBuilder(const ServerManagerPage()), ), ], ), ], - ); + ), - Page handleCallback( - BuildContext context, - GoRouterState state, - ) { + // loggers page + GoRoute( + path: Routes.logs.localPath, + name: Routes.logs.name, + // builder: (context, state) => const LogsPage(), + pageBuilder: defaultPageBuilder(const LogsPage()), + ), + ], + ); + + Page handleCallback(BuildContext context, GoRouterState state) { // extract the code and state from the uri final code = state.uri.queryParameters['code']; final stateParam = state.uri.queryParameters['state']; - debugPrint('deep linking callback: code: $code, state: $stateParam'); + appLogger.fine('deep linking callback: code: $code, state: $stateParam'); - var callbackPage = - CallbackPage(code: code, state: stateParam, key: ValueKey(stateParam)); + var callbackPage = CallbackPage( + code: code, + state: stateParam, + key: ValueKey(stateParam), + ); return buildPageWithDefaultTransition( context: context, state: state, diff --git a/lib/router/scaffold_with_nav_bar.dart b/lib/router/scaffold_with_nav_bar.dart index 467ec71..5f0b026 100644 --- a/lib/router/scaffold_with_nav_bar.dart +++ b/lib/router/scaffold_with_nav_bar.dart @@ -2,11 +2,15 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:miniplayer/miniplayer.dart'; +import 'package:vaani/api/library_provider.dart' show currentLibraryProvider; import 'package:vaani/features/explore/providers/search_controller.dart'; import 'package:vaani/features/player/providers/player_form.dart'; import 'package:vaani/features/player/view/audiobook_player.dart'; import 'package:vaani/features/player/view/player_when_expanded.dart'; +import 'package:vaani/features/you/view/widgets/library_switch_chip.dart'; +import 'package:vaani/main.dart'; import 'package:vaani/router/router.dart'; +import 'package:vaani/shared/icons/abs_icons.dart' show AbsIcons; // stack to track changes in navigationShell.currentIndex // home is always at index 0 and at the start and should be the last before popping @@ -19,10 +23,8 @@ const bottomBarHeight = 64; /// BottomNavigationBar, where [child] is placed in the body of the Scaffold. class ScaffoldWithNavBar extends HookConsumerWidget { /// Constructs an [ScaffoldWithNavBar]. - const ScaffoldWithNavBar({ - required this.navigationShell, - Key? key, - }) : super(key: key ?? const ValueKey('ScaffoldWithNavBar')); + const ScaffoldWithNavBar({required this.navigationShell, Key? key}) + : super(key: key ?? const ValueKey('ScaffoldWithNavBar')); /// The navigation shell and container for the branch Navigators. final StatefulNavigationShell navigationShell; @@ -31,10 +33,11 @@ class ScaffoldWithNavBar extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { // playerExpandProgress is used to animate bottom navigation bar to opacity 0 and slide down when player is expanded // final playerProgress = - // useValueListenable(ref.watch(playerExpandProgressNotifierProvider)); + // useValueListenable(ref.watch(playerExpandProgressProvider)); final playerProgress = ref.watch(playerHeightProvider); final playerMaxHeight = MediaQuery.of(context).size.height; - var percentExpandedMiniPlayer = (playerProgress - playerMinHeight) / + var percentExpandedMiniPlayer = + (playerProgress - playerMinHeight) / (playerMaxHeight - playerMinHeight); // Clamp the value between 0 and 1 percentExpandedMiniPlayer = percentExpandedMiniPlayer.clamp(0.0, 1.0); @@ -42,15 +45,13 @@ class ScaffoldWithNavBar extends HookConsumerWidget { onBackButtonPressed() async { final isPlayerExpanded = playerProgress != playerMinHeight; - debugPrint( + appLogger.fine( 'BackButtonListener: Back button pressed, isPlayerExpanded: $isPlayerExpanded, stack: $navigationShellStack, pendingPlayerModals: $pendingPlayerModals', ); // close miniplayer if it is open if (isPlayerExpanded && pendingPlayerModals == 0) { - debugPrint( - 'BackButtonListener: closing the player', - ); + appLogger.fine('BackButtonListener: closing the player'); audioBookMiniplayerController.animateToHeight(state: PanelState.MIN); return true; } @@ -59,7 +60,7 @@ class ScaffoldWithNavBar extends HookConsumerWidget { final canPop = GoRouter.of(context).canPop(); if (canPop) { - debugPrint( + appLogger.fine( 'BackButtonListener: passing it to the router as canPop is true', ); return false; @@ -69,7 +70,7 @@ class ScaffoldWithNavBar extends HookConsumerWidget { // pop the last index from the stack and navigate to it final index = navigationShellStack.last; navigationShellStack.remove(index); - debugPrint('BackButtonListener: popping the stack, index: $index'); + appLogger.fine('BackButtonListener: popping the stack, index: $index'); // if the stack is empty, navigate to home else navigate to the last index if (navigationShellStack.isNotEmpty) { @@ -79,12 +80,12 @@ class ScaffoldWithNavBar extends HookConsumerWidget { } if (navigationShell.currentIndex != 0) { // if the stack is empty and the current branch is not home, navigate to home - debugPrint('BackButtonListener: navigating to home'); + appLogger.fine('BackButtonListener: navigating to home'); navigationShell.goBranch(0); return true; } - debugPrint('BackButtonListener: passing it to the router'); + appLogger.fine('BackButtonListener: passing it to the router'); return false; } @@ -92,12 +93,7 @@ class ScaffoldWithNavBar extends HookConsumerWidget { return BackButtonListener( onBackButtonPressed: onBackButtonPressed, child: Scaffold( - body: Stack( - children: [ - navigationShell, - const AudiobookPlayer(), - ], - ), + body: Stack(children: [navigationShell, const AudiobookPlayer()]), bottomNavigationBar: Opacity( // Opacity is interpolated from 1 to 0 when player is expanded opacity: 1 - percentExpandedMiniPlayer, @@ -110,17 +106,36 @@ class ScaffoldWithNavBar extends HookConsumerWidget { // world scenario, the items would most likely be generated from the // branches of the shell route, which can be fetched using // `navigationShell.route.branches`. - destinations: _navigationItems - .map( - (item) => NavigationDestination( - icon: Icon(item.icon), - selectedIcon: item.activeIcon != null - ? Icon(item.activeIcon) - : Icon(item.icon), - label: item.name, - ), - ) - .toList(), + destinations: _navigationItems.map((item) { + final isDestinationLibrary = item.name == 'Library'; + var currentLibrary = ref.watch(currentLibraryProvider).value; + final libraryIcon = AbsIcons.getIconByName(currentLibrary?.icon); + final destinationWidget = NavigationDestination( + icon: Icon( + isDestinationLibrary ? libraryIcon ?? item.icon : item.icon, + ), + selectedIcon: Icon( + isDestinationLibrary + ? libraryIcon ?? item.activeIcon + : item.activeIcon, + ), + label: isDestinationLibrary + ? currentLibrary?.name ?? item.name + : item.name, + tooltip: item.tooltip, + ); + if (isDestinationLibrary) { + return GestureDetector( + onSecondaryTap: () => showLibrarySwitcher(context, ref), + onDoubleTap: () => showLibrarySwitcher(context, ref), + child: + destinationWidget, // Wrap the actual NavigationDestination + ); + } else { + // Return the unwrapped destination for other items + return destinationWidget; + } + }).toList(), selectedIndex: navigationShell.currentIndex, onDestinationSelected: (int index) => _onTap(context, index, ref), ), @@ -149,12 +164,11 @@ class ScaffoldWithNavBar extends HookConsumerWidget { navigationShellStack.remove(index); } navigationShellStack.add(index); - debugPrint('Tapped index: $index, stack: $navigationShellStack'); + appLogger.fine('Tapped index: $index, stack: $navigationShellStack'); // Check if the current branch is the same as the branch that was tapped. - // If it is, debugPrint a message to the console. if (index == navigationShell.currentIndex) { - debugPrint('Tapped the current branch'); + appLogger.fine('Tapped the current branch'); // if current branch is explore, open the search view if (index == 2) { @@ -191,16 +205,19 @@ const _navigationItems = [ name: 'Library', icon: Icons.book_outlined, activeIcon: Icons.book, + tooltip: 'Browse your library', ), _NavigationItem( name: 'Explore', icon: Icons.search_outlined, activeIcon: Icons.search, + tooltip: 'Search and Explore', ), _NavigationItem( name: 'You', icon: Icons.account_circle_outlined, activeIcon: Icons.account_circle, + tooltip: 'Your Profile and Settings', ), ]; @@ -208,10 +225,12 @@ class _NavigationItem { const _NavigationItem({ required this.name, required this.icon, - this.activeIcon, + required this.activeIcon, + this.tooltip, }); final String name; final IconData icon; - final IconData? activeIcon; + final IconData activeIcon; + final String? tooltip; } diff --git a/lib/router/transitions/slide.dart b/lib/router/transitions/slide.dart index 969edea..79c6364 100644 --- a/lib/router/transitions/slide.dart +++ b/lib/router/transitions/slide.dart @@ -33,29 +33,26 @@ CustomTransitionPage buildPageWithDefaultTransition({ child: child, transitionsBuilder: (context, animation, secondaryAnimation, child) => FadeTransition( - opacity: animation, - child: SlideTransition( - position: animation.drive( - Tween( - begin: const Offset(0, 1.50), - end: Offset.zero, - ).chain( - CurveTween(curve: Curves.easeOut), + opacity: animation, + child: SlideTransition( + position: animation.drive( + Tween( + begin: const Offset(0, 1.50), + end: Offset.zero, + ).chain(CurveTween(curve: Curves.easeOut)), + ), + child: child, ), ), - child: child, - ), - ), ); } Page Function(BuildContext, GoRouterState) defaultPageBuilder( Widget child, -) => - (BuildContext context, GoRouterState state) { - return buildPageWithDefaultTransition( - context: context, - state: state, - child: child, - ); - }; +) => (BuildContext context, GoRouterState state) { + return buildPageWithDefaultTransition( + context: context, + state: state, + child: child, + ); +}; diff --git a/lib/settings/api_settings_provider.dart b/lib/settings/api_settings_provider.dart index ecd297e..132ac7b 100644 --- a/lib/settings/api_settings_provider.dart +++ b/lib/settings/api_settings_provider.dart @@ -4,6 +4,7 @@ import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:vaani/db/available_boxes.dart'; import 'package:vaani/settings/models/api_settings.dart' as model; +import 'package:vaani/shared/extensions/obfuscation.dart'; part 'api_settings_provider.g.dart'; @@ -16,9 +17,10 @@ class ApiSettings extends _$ApiSettings { @override model.ApiSettings build() { state = readFromBoxOrCreate(); - ref.listenSelf((_, __) { + listenSelf((_, __) { writeToBox(); }); + return state; } @@ -33,7 +35,7 @@ class ApiSettings extends _$ApiSettings { activeServer: foundSettings.activeUser?.server, ); } - _logger.fine('found api settings in box: $foundSettings'); + _logger.fine('found api settings in box: ${foundSettings.obfuscate()}'); return foundSettings; } else { // create a new settings object @@ -47,7 +49,7 @@ class ApiSettings extends _$ApiSettings { void writeToBox() { _box.clear(); _box.add(state); - _logger.fine('wrote api settings to box: $state'); + _logger.fine('wrote api settings to box: ${state.obfuscate()}'); } void updateState(model.ApiSettings newSettings, {bool force = false}) { diff --git a/lib/settings/api_settings_provider.g.dart b/lib/settings/api_settings_provider.g.dart index 0bfacbf..752dfcc 100644 --- a/lib/settings/api_settings_provider.g.dart +++ b/lib/settings/api_settings_provider.g.dart @@ -6,20 +6,57 @@ part of 'api_settings_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$apiSettingsHash() => r'26e7e09e7369bac9fbf0589da9fd97d1f15b7926'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning -/// See also [ApiSettings]. @ProviderFor(ApiSettings) -final apiSettingsProvider = - NotifierProvider.internal( - ApiSettings.new, - name: r'apiSettingsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$apiSettingsHash, - dependencies: null, - allTransitiveDependencies: null, -); +final apiSettingsProvider = ApiSettingsProvider._(); -typedef _$ApiSettings = Notifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +final class ApiSettingsProvider + extends $NotifierProvider { + ApiSettingsProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'apiSettingsProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$apiSettingsHash(); + + @$internal + @override + ApiSettings create() => ApiSettings(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(model.ApiSettings value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$apiSettingsHash() => r'02af850985338eade33d76fc9965808bed548290'; + +abstract class _$ApiSettings extends $Notifier { + model.ApiSettings build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + model.ApiSettings, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/settings/app_settings_provider.dart b/lib/settings/app_settings_provider.dart index 6d61774..ae843c3 100644 --- a/lib/settings/app_settings_provider.dart +++ b/lib/settings/app_settings_provider.dart @@ -19,8 +19,10 @@ model.AppSettings loadOrCreateAppSettings() { settings = _box.getAt(0); _logger.fine('found settings in box: $settings'); } catch (e) { - _logger.warning('error reading settings from box: $e' - '\nclearing box'); + _logger.warning( + 'error reading settings from box: $e' + '\nclearing box', + ); _box.clear(); } } else { @@ -34,7 +36,7 @@ class AppSettings extends _$AppSettings { @override model.AppSettings build() { state = loadOrCreateAppSettings(); - ref.listenSelf((_, __) { + listenSelf((_, __) { writeToBox(); }); return state; @@ -47,11 +49,6 @@ class AppSettings extends _$AppSettings { _logger.fine('wrote settings to box: $state'); } - void toggleDarkMode() { - state = state.copyWith - .themeSettings(isDarkMode: !state.themeSettings.isDarkMode); - } - void update(model.AppSettings newSettings) { state = newSettings; } diff --git a/lib/settings/app_settings_provider.g.dart b/lib/settings/app_settings_provider.g.dart index df95738..8cb4a0f 100644 --- a/lib/settings/app_settings_provider.g.dart +++ b/lib/settings/app_settings_provider.g.dart @@ -6,37 +6,112 @@ part of 'app_settings_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$appSettingsHash() => r'f51d55f117692d4fb9f4b4febf02906c0953d334'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning -/// See also [AppSettings]. @ProviderFor(AppSettings) -final appSettingsProvider = - NotifierProvider.internal( - AppSettings.new, - name: r'appSettingsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$appSettingsHash, - dependencies: null, - allTransitiveDependencies: null, -); +final appSettingsProvider = AppSettingsProvider._(); + +final class AppSettingsProvider + extends $NotifierProvider { + AppSettingsProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'appSettingsProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$appSettingsHash(); + + @$internal + @override + AppSettings create() => AppSettings(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(model.AppSettings value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$appSettingsHash() => r'744d7e0157eb3b089c4187b35b845fc78547a44e'; + +abstract class _$AppSettings extends $Notifier { + model.AppSettings build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + model.AppSettings, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} + +@ProviderFor(SleepTimerSettings) +final sleepTimerSettingsProvider = SleepTimerSettingsProvider._(); + +final class SleepTimerSettingsProvider + extends $NotifierProvider { + SleepTimerSettingsProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'sleepTimerSettingsProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$sleepTimerSettingsHash(); + + @$internal + @override + SleepTimerSettings create() => SleepTimerSettings(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(model.SleepTimerSettings value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} -typedef _$AppSettings = Notifier; String _$sleepTimerSettingsHash() => r'85bb3d3fb292b9a3a5b771d86e5fc57718519c69'; -/// See also [SleepTimerSettings]. -@ProviderFor(SleepTimerSettings) -final sleepTimerSettingsProvider = - NotifierProvider.internal( - SleepTimerSettings.new, - name: r'sleepTimerSettingsProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$sleepTimerSettingsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$SleepTimerSettings = Notifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +abstract class _$SleepTimerSettings + extends $Notifier { + model.SleepTimerSettings build(); + @$mustCallSuper + @override + void runBuild() { + final ref = + this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + model.SleepTimerSettings, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/settings/constants.dart b/lib/settings/constants.dart index 2429545..3b836d3 100644 --- a/lib/settings/constants.dart +++ b/lib/settings/constants.dart @@ -1,6 +1,5 @@ import 'package:flutter/foundation.dart' show immutable; - @immutable class AppMetadata { const AppMetadata._(); @@ -10,5 +9,10 @@ class AppMetadata { // for deeplinking static const String appScheme = 'vaani'; + static const version = '1.0.0'; + static const author = 'Dr.Blank'; + + static Uri githubRepo = Uri.parse('https://github.com/Dr-Blank/Vaani'); + static get appNameLowerCase => appName.toLowerCase().replaceAll(' ', '_'); } diff --git a/lib/settings/metadata/metadata_provider.dart b/lib/settings/metadata/metadata_provider.dart index 053a67b..a1ce12f 100644 --- a/lib/settings/metadata/metadata_provider.dart +++ b/lib/settings/metadata/metadata_provider.dart @@ -1,92 +1,93 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'metadata_provider.g.dart'; @Riverpod(keepAlive: true) -Future deviceName(DeviceNameRef ref) async { +Future deviceName(Ref ref) async { final data = await _getDeviceData(DeviceInfoPlugin()); // try different keys to get the device name return - // android - data['product'] ?? - // ios - data['name'] ?? - // linux - data['name'] ?? - // windows - data['computerName'] ?? - // macos - data['model'] ?? - // web - data['browserName'] ?? - 'Unknown name'; + // android + data['product'] ?? + // ios + data['name'] ?? + // linux + data['name'] ?? + // windows + data['computerName'] ?? + // macos + data['model'] ?? + // web + data['browserName'] ?? + 'Unknown name'; } @Riverpod(keepAlive: true) -Future deviceModel(DeviceModelRef ref) async { +Future deviceModel(Ref ref) async { final data = await _getDeviceData(DeviceInfoPlugin()); // try different keys to get the device model return - // android, eg: Google Pixel 4 + // android, eg: Google Pixel 4 + data['model'] ?? + // ios, eg: iPhone 12 Pro + data['name'] ?? + // linux, eg: Linux Mint 20.1 + data['name'] ?? + // windows, eg: Surface Pro 7 + data['productId'] ?? + // macos, eg: MacBook Pro (13-inch, M1, 2020) data['model'] ?? - // ios, eg: iPhone 12 Pro - data['name'] ?? - // linux, eg: Linux Mint 20.1 - data['name'] ?? - // windows, eg: Surface Pro 7 - data['productId'] ?? - // macos, eg: MacBook Pro (13-inch, M1, 2020) - data['model'] ?? - // web, eg: Chrome 87.0.4280.88 - data['browserName'] ?? - 'Unknown model'; + // web, eg: Chrome 87.0.4280.88 + data['browserName'] ?? + 'Unknown model'; } @Riverpod(keepAlive: true) -Future deviceSdkVersion(DeviceSdkVersionRef ref) async { +Future deviceSdkVersion(Ref ref) async { final data = await _getDeviceData(DeviceInfoPlugin()); // try different keys to get the device sdk version return - // android, eg: 30 - data['version.sdkInt']?.toString() ?? - // ios, eg: 14.4 - data['systemVersion'] ?? - // linux, eg: 5.4.0-66-generic - data['version'] ?? - // windows, eg: 10.0.19042 - data['displayVersion'] ?? - // macos, eg: 11.2.1 - data['osRelease'] ?? - // web, eg: 87.0.4280.88 - data['appVersion'] ?? - 'Unknown sdk version'; + // android, eg: 30 + data['version.sdkInt']?.toString() ?? + // ios, eg: 14.4 + data['systemVersion'] ?? + // linux, eg: 5.4.0-66-generic + data['version'] ?? + // windows, eg: 10.0.19042 + data['displayVersion'] ?? + // macos, eg: 11.2.1 + data['osRelease'] ?? + // web, eg: 87.0.4280.88 + data['appVersion'] ?? + 'Unknown sdk version'; } @Riverpod(keepAlive: true) -Future deviceManufacturer(DeviceManufacturerRef ref) async { +Future deviceManufacturer(Ref ref) async { final data = await _getDeviceData(DeviceInfoPlugin()); // try different keys to get the device manufacturer return - // android, eg: Google + // android, eg: Google + data['manufacturer'] ?? + // ios, eg: Apple data['manufacturer'] ?? - // ios, eg: Apple - data['manufacturer'] ?? - // linux, eg: Linux - data['idLike'] ?? - // windows, eg: Microsoft - data['productName'] ?? - // macos, eg: Apple - data['manufacturer'] ?? - // web, eg: Google Inc. - data['vendor'] ?? - 'Unknown manufacturer'; + // linux, eg: Linux + data['idLike'] ?? + // windows, eg: Microsoft + data['productName'] ?? + // macos, eg: Apple + data['manufacturer'] ?? + // web, eg: Google Inc. + data['vendor'] ?? + 'Unknown manufacturer'; } // copied from https://pub.dev/packages/device_info_plus/example @@ -233,25 +234,28 @@ Future> _getDeviceData( deviceData = _readWebBrowserInfo(await deviceInfoPlugin.webBrowserInfo); } else { deviceData = switch (defaultTargetPlatform) { - TargetPlatform.android => - _readAndroidBuildData(await deviceInfoPlugin.androidInfo), - TargetPlatform.iOS => - _readIosDeviceInfo(await deviceInfoPlugin.iosInfo), - TargetPlatform.linux => - _readLinuxDeviceInfo(await deviceInfoPlugin.linuxInfo), - TargetPlatform.windows => - _readWindowsDeviceInfo(await deviceInfoPlugin.windowsInfo), - TargetPlatform.macOS => - _readMacOsDeviceInfo(await deviceInfoPlugin.macOsInfo), + TargetPlatform.android => _readAndroidBuildData( + await deviceInfoPlugin.androidInfo, + ), + TargetPlatform.iOS => _readIosDeviceInfo( + await deviceInfoPlugin.iosInfo, + ), + TargetPlatform.linux => _readLinuxDeviceInfo( + await deviceInfoPlugin.linuxInfo, + ), + TargetPlatform.windows => _readWindowsDeviceInfo( + await deviceInfoPlugin.windowsInfo, + ), + TargetPlatform.macOS => _readMacOsDeviceInfo( + await deviceInfoPlugin.macOsInfo, + ), TargetPlatform.fuchsia => { - errorKey: 'Fuchsia platform isn\'t supported', - }, + errorKey: 'Fuchsia platform isn\'t supported', + }, }; } } on PlatformException { - deviceData = { - errorKey: 'Failed to get platform version.', - }; + deviceData = {errorKey: 'Failed to get platform version.'}; } return deviceData; } diff --git a/lib/settings/metadata/metadata_provider.g.dart b/lib/settings/metadata/metadata_provider.g.dart index 858c351..50c6f1f 100644 --- a/lib/settings/metadata/metadata_provider.g.dart +++ b/lib/settings/metadata/metadata_provider.g.dart @@ -6,64 +6,138 @@ part of 'metadata_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$deviceNameHash() => r'bc206a3a8c14f3da6e257e92e1ccdc79364f4e28'; +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning -/// See also [deviceName]. @ProviderFor(deviceName) -final deviceNameProvider = FutureProvider.internal( - deviceName, - name: r'deviceNameProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$deviceNameHash, - dependencies: null, - allTransitiveDependencies: null, -); +final deviceNameProvider = DeviceNameProvider._(); -typedef DeviceNameRef = FutureProviderRef; -String _$deviceModelHash() => r'3d7e8ef4a37b90f98e38dc8d5f16ca30f71e15b2'; +final class DeviceNameProvider + extends $FunctionalProvider, String, FutureOr> + with $FutureModifier, $FutureProvider { + DeviceNameProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'deviceNameProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$deviceNameHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return deviceName(ref); + } +} + +String _$deviceNameHash() => r'9e38adda74e70a91851a682f05228bd759356dcc'; -/// See also [deviceModel]. @ProviderFor(deviceModel) -final deviceModelProvider = FutureProvider.internal( - deviceModel, - name: r'deviceModelProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$deviceModelHash, - dependencies: null, - allTransitiveDependencies: null, -); +final deviceModelProvider = DeviceModelProvider._(); -typedef DeviceModelRef = FutureProviderRef; -String _$deviceSdkVersionHash() => r'501b01ae679e02fc5082feabea81cea0fa74afd7'; +final class DeviceModelProvider + extends $FunctionalProvider, String, FutureOr> + with $FutureModifier, $FutureProvider { + DeviceModelProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'deviceModelProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$deviceModelHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return deviceModel(ref); + } +} + +String _$deviceModelHash() => r'922b13d9e35b5b5c5b8e96f2f2c2ae594f4f41f2'; -/// See also [deviceSdkVersion]. @ProviderFor(deviceSdkVersion) -final deviceSdkVersionProvider = FutureProvider.internal( - deviceSdkVersion, - name: r'deviceSdkVersionProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$deviceSdkVersionHash, - dependencies: null, - allTransitiveDependencies: null, -); +final deviceSdkVersionProvider = DeviceSdkVersionProvider._(); -typedef DeviceSdkVersionRef = FutureProviderRef; -String _$deviceManufacturerHash() => - r'f0a57e6a92b551fbe266d0a6a29d35dc497882a9'; +final class DeviceSdkVersionProvider + extends $FunctionalProvider, String, FutureOr> + with $FutureModifier, $FutureProvider { + DeviceSdkVersionProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'deviceSdkVersionProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$deviceSdkVersionHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return deviceSdkVersion(ref); + } +} + +String _$deviceSdkVersionHash() => r'33178d80590808d1f4cca2be8a3b52c6f6724cac'; -/// See also [deviceManufacturer]. @ProviderFor(deviceManufacturer) -final deviceManufacturerProvider = FutureProvider.internal( - deviceManufacturer, - name: r'deviceManufacturerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$deviceManufacturerHash, - dependencies: null, - allTransitiveDependencies: null, -); +final deviceManufacturerProvider = DeviceManufacturerProvider._(); -typedef DeviceManufacturerRef = FutureProviderRef; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member +final class DeviceManufacturerProvider + extends $FunctionalProvider, String, FutureOr> + with $FutureModifier, $FutureProvider { + DeviceManufacturerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'deviceManufacturerProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$deviceManufacturerHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return deviceManufacturer(ref); + } +} + +String _$deviceManufacturerHash() => + r'39250767deb8635fa7c7e18bae23576b9b863e04'; diff --git a/lib/settings/models/api_settings.dart b/lib/settings/models/api_settings.dart index 6410938..dd7aada 100644 --- a/lib/settings/models/api_settings.dart +++ b/lib/settings/models/api_settings.dart @@ -11,7 +11,7 @@ part 'api_settings.g.dart'; /// /// all settings that are needed to interact with the server are stored here @freezed -class ApiSettings with _$ApiSettings { +sealed class ApiSettings with _$ApiSettings { const factory ApiSettings({ AudiobookShelfServer? activeServer, AuthenticatedUser? activeUser, diff --git a/lib/settings/models/api_settings.freezed.dart b/lib/settings/models/api_settings.freezed.dart index 2f3c6e5..6669a2b 100644 --- a/lib/settings/models/api_settings.freezed.dart +++ b/lib/settings/models/api_settings.freezed.dart @@ -1,5 +1,5 @@ -// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark @@ -9,238 +9,317 @@ part of 'api_settings.dart'; // FreezedGenerator // ************************************************************************** +// dart format off T _$identity(T value) => value; -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -ApiSettings _$ApiSettingsFromJson(Map json) { - return _ApiSettings.fromJson(json); -} - /// @nodoc mixin _$ApiSettings { - AudiobookShelfServer? get activeServer => throw _privateConstructorUsedError; - AuthenticatedUser? get activeUser => throw _privateConstructorUsedError; - String? get activeLibraryId => throw _privateConstructorUsedError; + + AudiobookShelfServer? get activeServer; AuthenticatedUser? get activeUser; String? get activeLibraryId; +/// Create a copy of ApiSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ApiSettingsCopyWith get copyWith => _$ApiSettingsCopyWithImpl(this as ApiSettings, _$identity); /// Serializes this ApiSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ApiSettings&&(identical(other.activeServer, activeServer) || other.activeServer == activeServer)&&(identical(other.activeUser, activeUser) || other.activeUser == activeUser)&&(identical(other.activeLibraryId, activeLibraryId) || other.activeLibraryId == activeLibraryId)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,activeServer,activeUser,activeLibraryId); + +@override +String toString() { + return 'ApiSettings(activeServer: $activeServer, activeUser: $activeUser, activeLibraryId: $activeLibraryId)'; +} + - /// Create a copy of ApiSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ApiSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $ApiSettingsCopyWith<$Res> { - factory $ApiSettingsCopyWith( - ApiSettings value, $Res Function(ApiSettings) then) = - _$ApiSettingsCopyWithImpl<$Res, ApiSettings>; - @useResult - $Res call( - {AudiobookShelfServer? activeServer, - AuthenticatedUser? activeUser, - String? activeLibraryId}); +abstract mixin class $ApiSettingsCopyWith<$Res> { + factory $ApiSettingsCopyWith(ApiSettings value, $Res Function(ApiSettings) _then) = _$ApiSettingsCopyWithImpl; +@useResult +$Res call({ + AudiobookShelfServer? activeServer, AuthenticatedUser? activeUser, String? activeLibraryId +}); + + +$AudiobookShelfServerCopyWith<$Res>? get activeServer;$AuthenticatedUserCopyWith<$Res>? get activeUser; - $AudiobookShelfServerCopyWith<$Res>? get activeServer; - $AuthenticatedUserCopyWith<$Res>? get activeUser; } - /// @nodoc -class _$ApiSettingsCopyWithImpl<$Res, $Val extends ApiSettings> +class _$ApiSettingsCopyWithImpl<$Res> implements $ApiSettingsCopyWith<$Res> { - _$ApiSettingsCopyWithImpl(this._value, this._then); + _$ApiSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final ApiSettings _self; + final $Res Function(ApiSettings) _then; - /// Create a copy of ApiSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? activeServer = freezed, - Object? activeUser = freezed, - Object? activeLibraryId = freezed, - }) { - return _then(_value.copyWith( - activeServer: freezed == activeServer - ? _value.activeServer - : activeServer // ignore: cast_nullable_to_non_nullable - as AudiobookShelfServer?, - activeUser: freezed == activeUser - ? _value.activeUser - : activeUser // ignore: cast_nullable_to_non_nullable - as AuthenticatedUser?, - activeLibraryId: freezed == activeLibraryId - ? _value.activeLibraryId - : activeLibraryId // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); +/// Create a copy of ApiSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? activeServer = freezed,Object? activeUser = freezed,Object? activeLibraryId = freezed,}) { + return _then(_self.copyWith( +activeServer: freezed == activeServer ? _self.activeServer : activeServer // ignore: cast_nullable_to_non_nullable +as AudiobookShelfServer?,activeUser: freezed == activeUser ? _self.activeUser : activeUser // ignore: cast_nullable_to_non_nullable +as AuthenticatedUser?,activeLibraryId: freezed == activeLibraryId ? _self.activeLibraryId : activeLibraryId // ignore: cast_nullable_to_non_nullable +as String?, + )); +} +/// Create a copy of ApiSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$AudiobookShelfServerCopyWith<$Res>? get activeServer { + if (_self.activeServer == null) { + return null; } - /// Create a copy of ApiSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $AudiobookShelfServerCopyWith<$Res>? get activeServer { - if (_value.activeServer == null) { - return null; - } - - return $AudiobookShelfServerCopyWith<$Res>(_value.activeServer!, (value) { - return _then(_value.copyWith(activeServer: value) as $Val); - }); + return $AudiobookShelfServerCopyWith<$Res>(_self.activeServer!, (value) { + return _then(_self.copyWith(activeServer: value)); + }); +}/// Create a copy of ApiSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$AuthenticatedUserCopyWith<$Res>? get activeUser { + if (_self.activeUser == null) { + return null; } - /// Create a copy of ApiSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $AuthenticatedUserCopyWith<$Res>? get activeUser { - if (_value.activeUser == null) { - return null; - } - - return $AuthenticatedUserCopyWith<$Res>(_value.activeUser!, (value) { - return _then(_value.copyWith(activeUser: value) as $Val); - }); - } + return $AuthenticatedUserCopyWith<$Res>(_self.activeUser!, (value) { + return _then(_self.copyWith(activeUser: value)); + }); +} } -/// @nodoc -abstract class _$$ApiSettingsImplCopyWith<$Res> - implements $ApiSettingsCopyWith<$Res> { - factory _$$ApiSettingsImplCopyWith( - _$ApiSettingsImpl value, $Res Function(_$ApiSettingsImpl) then) = - __$$ApiSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {AudiobookShelfServer? activeServer, - AuthenticatedUser? activeUser, - String? activeLibraryId}); - @override - $AudiobookShelfServerCopyWith<$Res>? get activeServer; - @override - $AuthenticatedUserCopyWith<$Res>? get activeUser; +/// Adds pattern-matching-related methods to [ApiSettings]. +extension ApiSettingsPatterns on ApiSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ApiSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ApiSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ApiSettings value) $default,){ +final _that = this; +switch (_that) { +case _ApiSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ApiSettings value)? $default,){ +final _that = this; +switch (_that) { +case _ApiSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( AudiobookShelfServer? activeServer, AuthenticatedUser? activeUser, String? activeLibraryId)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ApiSettings() when $default != null: +return $default(_that.activeServer,_that.activeUser,_that.activeLibraryId);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( AudiobookShelfServer? activeServer, AuthenticatedUser? activeUser, String? activeLibraryId) $default,) {final _that = this; +switch (_that) { +case _ApiSettings(): +return $default(_that.activeServer,_that.activeUser,_that.activeLibraryId);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( AudiobookShelfServer? activeServer, AuthenticatedUser? activeUser, String? activeLibraryId)? $default,) {final _that = this; +switch (_that) { +case _ApiSettings() when $default != null: +return $default(_that.activeServer,_that.activeUser,_that.activeLibraryId);case _: + return null; + +} } -/// @nodoc -class __$$ApiSettingsImplCopyWithImpl<$Res> - extends _$ApiSettingsCopyWithImpl<$Res, _$ApiSettingsImpl> - implements _$$ApiSettingsImplCopyWith<$Res> { - __$$ApiSettingsImplCopyWithImpl( - _$ApiSettingsImpl _value, $Res Function(_$ApiSettingsImpl) _then) - : super(_value, _then); - - /// Create a copy of ApiSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? activeServer = freezed, - Object? activeUser = freezed, - Object? activeLibraryId = freezed, - }) { - return _then(_$ApiSettingsImpl( - activeServer: freezed == activeServer - ? _value.activeServer - : activeServer // ignore: cast_nullable_to_non_nullable - as AudiobookShelfServer?, - activeUser: freezed == activeUser - ? _value.activeUser - : activeUser // ignore: cast_nullable_to_non_nullable - as AuthenticatedUser?, - activeLibraryId: freezed == activeLibraryId - ? _value.activeLibraryId - : activeLibraryId // ignore: cast_nullable_to_non_nullable - as String?, - )); - } } /// @nodoc @JsonSerializable() -class _$ApiSettingsImpl implements _ApiSettings { - const _$ApiSettingsImpl( - {this.activeServer, this.activeUser, this.activeLibraryId}); - factory _$ApiSettingsImpl.fromJson(Map json) => - _$$ApiSettingsImplFromJson(json); +class _ApiSettings implements ApiSettings { + const _ApiSettings({this.activeServer, this.activeUser, this.activeLibraryId}); + factory _ApiSettings.fromJson(Map json) => _$ApiSettingsFromJson(json); - @override - final AudiobookShelfServer? activeServer; - @override - final AuthenticatedUser? activeUser; - @override - final String? activeLibraryId; +@override final AudiobookShelfServer? activeServer; +@override final AuthenticatedUser? activeUser; +@override final String? activeLibraryId; - @override - String toString() { - return 'ApiSettings(activeServer: $activeServer, activeUser: $activeUser, activeLibraryId: $activeLibraryId)'; - } +/// Create a copy of ApiSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ApiSettingsCopyWith<_ApiSettings> get copyWith => __$ApiSettingsCopyWithImpl<_ApiSettings>(this, _$identity); - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ApiSettingsImpl && - (identical(other.activeServer, activeServer) || - other.activeServer == activeServer) && - (identical(other.activeUser, activeUser) || - other.activeUser == activeUser) && - (identical(other.activeLibraryId, activeLibraryId) || - other.activeLibraryId == activeLibraryId)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => - Object.hash(runtimeType, activeServer, activeUser, activeLibraryId); - - /// Create a copy of ApiSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ApiSettingsImplCopyWith<_$ApiSettingsImpl> get copyWith => - __$$ApiSettingsImplCopyWithImpl<_$ApiSettingsImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ApiSettingsImplToJson( - this, - ); - } +@override +Map toJson() { + return _$ApiSettingsToJson(this, ); } -abstract class _ApiSettings implements ApiSettings { - const factory _ApiSettings( - {final AudiobookShelfServer? activeServer, - final AuthenticatedUser? activeUser, - final String? activeLibraryId}) = _$ApiSettingsImpl; - - factory _ApiSettings.fromJson(Map json) = - _$ApiSettingsImpl.fromJson; - - @override - AudiobookShelfServer? get activeServer; - @override - AuthenticatedUser? get activeUser; - @override - String? get activeLibraryId; - - /// Create a copy of ApiSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ApiSettingsImplCopyWith<_$ApiSettingsImpl> get copyWith => - throw _privateConstructorUsedError; +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ApiSettings&&(identical(other.activeServer, activeServer) || other.activeServer == activeServer)&&(identical(other.activeUser, activeUser) || other.activeUser == activeUser)&&(identical(other.activeLibraryId, activeLibraryId) || other.activeLibraryId == activeLibraryId)); } + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,activeServer,activeUser,activeLibraryId); + +@override +String toString() { + return 'ApiSettings(activeServer: $activeServer, activeUser: $activeUser, activeLibraryId: $activeLibraryId)'; +} + + +} + +/// @nodoc +abstract mixin class _$ApiSettingsCopyWith<$Res> implements $ApiSettingsCopyWith<$Res> { + factory _$ApiSettingsCopyWith(_ApiSettings value, $Res Function(_ApiSettings) _then) = __$ApiSettingsCopyWithImpl; +@override @useResult +$Res call({ + AudiobookShelfServer? activeServer, AuthenticatedUser? activeUser, String? activeLibraryId +}); + + +@override $AudiobookShelfServerCopyWith<$Res>? get activeServer;@override $AuthenticatedUserCopyWith<$Res>? get activeUser; + +} +/// @nodoc +class __$ApiSettingsCopyWithImpl<$Res> + implements _$ApiSettingsCopyWith<$Res> { + __$ApiSettingsCopyWithImpl(this._self, this._then); + + final _ApiSettings _self; + final $Res Function(_ApiSettings) _then; + +/// Create a copy of ApiSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? activeServer = freezed,Object? activeUser = freezed,Object? activeLibraryId = freezed,}) { + return _then(_ApiSettings( +activeServer: freezed == activeServer ? _self.activeServer : activeServer // ignore: cast_nullable_to_non_nullable +as AudiobookShelfServer?,activeUser: freezed == activeUser ? _self.activeUser : activeUser // ignore: cast_nullable_to_non_nullable +as AuthenticatedUser?,activeLibraryId: freezed == activeLibraryId ? _self.activeLibraryId : activeLibraryId // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +/// Create a copy of ApiSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$AudiobookShelfServerCopyWith<$Res>? get activeServer { + if (_self.activeServer == null) { + return null; + } + + return $AudiobookShelfServerCopyWith<$Res>(_self.activeServer!, (value) { + return _then(_self.copyWith(activeServer: value)); + }); +}/// Create a copy of ApiSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$AuthenticatedUserCopyWith<$Res>? get activeUser { + if (_self.activeUser == null) { + return null; + } + + return $AuthenticatedUserCopyWith<$Res>(_self.activeUser!, (value) { + return _then(_self.copyWith(activeUser: value)); + }); +} +} + +// dart format on diff --git a/lib/settings/models/api_settings.g.dart b/lib/settings/models/api_settings.g.dart index 568754a..13b6359 100644 --- a/lib/settings/models/api_settings.g.dart +++ b/lib/settings/models/api_settings.g.dart @@ -6,20 +6,19 @@ part of 'api_settings.dart'; // JsonSerializableGenerator // ************************************************************************** -_$ApiSettingsImpl _$$ApiSettingsImplFromJson(Map json) => - _$ApiSettingsImpl( - activeServer: json['activeServer'] == null - ? null - : AudiobookShelfServer.fromJson( - json['activeServer'] as Map), - activeUser: json['activeUser'] == null - ? null - : AuthenticatedUser.fromJson( - json['activeUser'] as Map), - activeLibraryId: json['activeLibraryId'] as String?, - ); +_ApiSettings _$ApiSettingsFromJson(Map json) => _ApiSettings( + activeServer: json['activeServer'] == null + ? null + : AudiobookShelfServer.fromJson( + json['activeServer'] as Map, + ), + activeUser: json['activeUser'] == null + ? null + : AuthenticatedUser.fromJson(json['activeUser'] as Map), + activeLibraryId: json['activeLibraryId'] as String?, +); -Map _$$ApiSettingsImplToJson(_$ApiSettingsImpl instance) => +Map _$ApiSettingsToJson(_ApiSettings instance) => { 'activeServer': instance.activeServer, 'activeUser': instance.activeUser, diff --git a/lib/settings/models/app_settings.dart b/lib/settings/models/app_settings.dart index 80262ea..41fddd0 100644 --- a/lib/settings/models/app_settings.dart +++ b/lib/settings/models/app_settings.dart @@ -10,7 +10,7 @@ part 'app_settings.g.dart'; /// /// only the visual settings are stored here @freezed -class AppSettings with _$AppSettings { +sealed class AppSettings with _$AppSettings { const factory AppSettings({ @Default(ThemeSettings()) ThemeSettings themeSettings, @Default(PlayerSettings()) PlayerSettings playerSettings, @@ -19,6 +19,7 @@ class AppSettings with _$AppSettings { @Default(NotificationSettings()) NotificationSettings notificationSettings, @Default(ShakeDetectionSettings()) ShakeDetectionSettings shakeDetectionSettings, + @Default(HomePageSettings()) HomePageSettings homePageSettings, }) = _AppSettings; factory AppSettings.fromJson(Map json) => @@ -26,9 +27,12 @@ class AppSettings with _$AppSettings { } @freezed -class ThemeSettings with _$ThemeSettings { +sealed class ThemeSettings with _$ThemeSettings { const factory ThemeSettings({ - @Default(true) bool isDarkMode, + @Default(ThemeMode.system) ThemeMode themeMode, + @Default(false) bool highContrast, + @Default(false) bool useMaterialThemeFromSystem, + @Default('#FF311B92') String customThemeColor, @Default(true) bool useMaterialThemeOnItemPage, @Default(true) bool useCurrentPlayerThemeThroughoutApp, }) = _ThemeSettings; @@ -38,7 +42,7 @@ class ThemeSettings with _$ThemeSettings { } @freezed -class PlayerSettings with _$PlayerSettings { +sealed class PlayerSettings with _$PlayerSettings { const factory PlayerSettings({ @Default(MinimizedPlayerSettings()) MinimizedPlayerSettings miniPlayerSettings, @@ -61,7 +65,7 @@ class PlayerSettings with _$PlayerSettings { } @freezed -class ExpandedPlayerSettings with _$ExpandedPlayerSettings { +sealed class ExpandedPlayerSettings with _$ExpandedPlayerSettings { const factory ExpandedPlayerSettings({ @Default(false) bool showTotalProgress, @Default(true) bool showChapterProgress, @@ -72,28 +76,25 @@ class ExpandedPlayerSettings with _$ExpandedPlayerSettings { } @freezed -class MinimizedPlayerSettings with _$MinimizedPlayerSettings { - const factory MinimizedPlayerSettings({ - @Default(false) bool useChapterInfo, - }) = _MinimizedPlayerSettings; +sealed class MinimizedPlayerSettings with _$MinimizedPlayerSettings { + const factory MinimizedPlayerSettings({@Default(false) bool useChapterInfo}) = + _MinimizedPlayerSettings; factory MinimizedPlayerSettings.fromJson(Map json) => _$MinimizedPlayerSettingsFromJson(json); } @freezed -class SleepTimerSettings with _$SleepTimerSettings { +sealed class SleepTimerSettings with _$SleepTimerSettings { const factory SleepTimerSettings({ @Default(Duration(minutes: 15)) Duration defaultDuration, - @Default( - [ - Duration(minutes: 5), - Duration(minutes: 10), - Duration(minutes: 15), - Duration(minutes: 20), - Duration(minutes: 30), - ], - ) + @Default([ + Duration(minutes: 5), + Duration(minutes: 10), + Duration(minutes: 15), + Duration(minutes: 20), + Duration(minutes: 30), + ]) List presetDurations, @Default(Duration(minutes: 100)) Duration maxDuration, @Default(false) bool fadeOutAudio, @@ -130,7 +131,7 @@ class SleepTimerSettings with _$SleepTimerSettings { } @freezed -class DownloadSettings with _$DownloadSettings { +sealed class DownloadSettings with _$DownloadSettings { const factory DownloadSettings({ @Default(true) bool requiresWiFi, @Default(3) int retries, @@ -145,21 +146,19 @@ class DownloadSettings with _$DownloadSettings { } @freezed -class NotificationSettings with _$NotificationSettings { +sealed class NotificationSettings with _$NotificationSettings { const factory NotificationSettings({ @Default(Duration(seconds: 30)) Duration fastForwardInterval, @Default(Duration(seconds: 10)) Duration rewindInterval, @Default(true) bool progressBarIsChapterProgress, @Default('\$bookTitle') String primaryTitle, @Default('\$author') String secondaryTitle, - @Default( - [ - NotificationMediaControl.rewind, - NotificationMediaControl.fastForward, - NotificationMediaControl.skipToPreviousChapter, - NotificationMediaControl.skipToNextChapter, - ], - ) + @Default([ + NotificationMediaControl.rewind, + NotificationMediaControl.fastForward, + NotificationMediaControl.skipToPreviousChapter, + NotificationMediaControl.skipToNextChapter, + ]) List mediaControls, }) = _NotificationSettings; @@ -192,7 +191,7 @@ enum NotificationMediaControl { /// Shake Detection Settings @freezed -class ShakeDetectionSettings with _$ShakeDetectionSettings { +sealed class ShakeDetectionSettings with _$ShakeDetectionSettings { const factory ShakeDetectionSettings({ @Default(true) bool isEnabled, @Default(ShakeDirection.horizontal) ShakeDirection direction, @@ -218,12 +217,19 @@ class ShakeDetectionSettings with _$ShakeDetectionSettings { enum ShakeDirection { horizontal, vertical } -enum ShakeAction { - none, - playPause, - resetSleepTimer, - fastForward, - rewind, -} +enum ShakeAction { none, playPause, resetSleepTimer, fastForward, rewind } enum ShakeDetectedFeedback { vibrate, beep } + +@freezed +sealed class HomePageSettings with _$HomePageSettings { + const factory HomePageSettings({ + @Default(true) bool showPlayButtonOnContinueListeningShelf, + @Default(false) bool showPlayButtonOnContinueSeriesShelf, + @Default(false) bool showPlayButtonOnAllRemainingShelves, + @Default(false) bool showPlayButtonOnListenAgainShelf, + }) = _HomePageSettings; + + factory HomePageSettings.fromJson(Map json) => + _$HomePageSettingsFromJson(json); +} diff --git a/lib/settings/models/app_settings.freezed.dart b/lib/settings/models/app_settings.freezed.dart index 3f8fd48..3d17503 100644 --- a/lib/settings/models/app_settings.freezed.dart +++ b/lib/settings/models/app_settings.freezed.dart @@ -1,5 +1,5 @@ -// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark @@ -9,2747 +9,2951 @@ part of 'app_settings.dart'; // FreezedGenerator // ************************************************************************** +// dart format off T _$identity(T value) => value; -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -AppSettings _$AppSettingsFromJson(Map json) { - return _AppSettings.fromJson(json); -} - /// @nodoc mixin _$AppSettings { - ThemeSettings get themeSettings => throw _privateConstructorUsedError; - PlayerSettings get playerSettings => throw _privateConstructorUsedError; - SleepTimerSettings get sleepTimerSettings => - throw _privateConstructorUsedError; - DownloadSettings get downloadSettings => throw _privateConstructorUsedError; - NotificationSettings get notificationSettings => - throw _privateConstructorUsedError; - ShakeDetectionSettings get shakeDetectionSettings => - throw _privateConstructorUsedError; + + ThemeSettings get themeSettings; PlayerSettings get playerSettings; SleepTimerSettings get sleepTimerSettings; DownloadSettings get downloadSettings; NotificationSettings get notificationSettings; ShakeDetectionSettings get shakeDetectionSettings; HomePageSettings get homePageSettings; +/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$AppSettingsCopyWith get copyWith => _$AppSettingsCopyWithImpl(this as AppSettings, _$identity); /// Serializes this AppSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.themeSettings, themeSettings) || other.themeSettings == themeSettings)&&(identical(other.playerSettings, playerSettings) || other.playerSettings == playerSettings)&&(identical(other.sleepTimerSettings, sleepTimerSettings) || other.sleepTimerSettings == sleepTimerSettings)&&(identical(other.downloadSettings, downloadSettings) || other.downloadSettings == downloadSettings)&&(identical(other.notificationSettings, notificationSettings) || other.notificationSettings == notificationSettings)&&(identical(other.shakeDetectionSettings, shakeDetectionSettings) || other.shakeDetectionSettings == shakeDetectionSettings)&&(identical(other.homePageSettings, homePageSettings) || other.homePageSettings == homePageSettings)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,themeSettings,playerSettings,sleepTimerSettings,downloadSettings,notificationSettings,shakeDetectionSettings,homePageSettings); + +@override +String toString() { + return 'AppSettings(themeSettings: $themeSettings, playerSettings: $playerSettings, sleepTimerSettings: $sleepTimerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings, shakeDetectionSettings: $shakeDetectionSettings, homePageSettings: $homePageSettings)'; +} + - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $AppSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $AppSettingsCopyWith<$Res> { - factory $AppSettingsCopyWith( - AppSettings value, $Res Function(AppSettings) then) = - _$AppSettingsCopyWithImpl<$Res, AppSettings>; - @useResult - $Res call( - {ThemeSettings themeSettings, - PlayerSettings playerSettings, - SleepTimerSettings sleepTimerSettings, - DownloadSettings downloadSettings, - NotificationSettings notificationSettings, - ShakeDetectionSettings shakeDetectionSettings}); +abstract mixin class $AppSettingsCopyWith<$Res> { + factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl; +@useResult +$Res call({ + ThemeSettings themeSettings, PlayerSettings playerSettings, SleepTimerSettings sleepTimerSettings, DownloadSettings downloadSettings, NotificationSettings notificationSettings, ShakeDetectionSettings shakeDetectionSettings, HomePageSettings homePageSettings +}); + + +$ThemeSettingsCopyWith<$Res> get themeSettings;$PlayerSettingsCopyWith<$Res> get playerSettings;$SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings;$DownloadSettingsCopyWith<$Res> get downloadSettings;$NotificationSettingsCopyWith<$Res> get notificationSettings;$ShakeDetectionSettingsCopyWith<$Res> get shakeDetectionSettings;$HomePageSettingsCopyWith<$Res> get homePageSettings; - $ThemeSettingsCopyWith<$Res> get themeSettings; - $PlayerSettingsCopyWith<$Res> get playerSettings; - $SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings; - $DownloadSettingsCopyWith<$Res> get downloadSettings; - $NotificationSettingsCopyWith<$Res> get notificationSettings; - $ShakeDetectionSettingsCopyWith<$Res> get shakeDetectionSettings; } - /// @nodoc -class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings> +class _$AppSettingsCopyWithImpl<$Res> implements $AppSettingsCopyWith<$Res> { - _$AppSettingsCopyWithImpl(this._value, this._then); + _$AppSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final AppSettings _self; + final $Res Function(AppSettings) _then; - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? themeSettings = null, - Object? playerSettings = null, - Object? sleepTimerSettings = null, - Object? downloadSettings = null, - Object? notificationSettings = null, - Object? shakeDetectionSettings = null, - }) { - return _then(_value.copyWith( - themeSettings: null == themeSettings - ? _value.themeSettings - : themeSettings // ignore: cast_nullable_to_non_nullable - as ThemeSettings, - playerSettings: null == playerSettings - ? _value.playerSettings - : playerSettings // ignore: cast_nullable_to_non_nullable - as PlayerSettings, - sleepTimerSettings: null == sleepTimerSettings - ? _value.sleepTimerSettings - : sleepTimerSettings // ignore: cast_nullable_to_non_nullable - as SleepTimerSettings, - downloadSettings: null == downloadSettings - ? _value.downloadSettings - : downloadSettings // ignore: cast_nullable_to_non_nullable - as DownloadSettings, - notificationSettings: null == notificationSettings - ? _value.notificationSettings - : notificationSettings // ignore: cast_nullable_to_non_nullable - as NotificationSettings, - shakeDetectionSettings: null == shakeDetectionSettings - ? _value.shakeDetectionSettings - : shakeDetectionSettings // ignore: cast_nullable_to_non_nullable - as ShakeDetectionSettings, - ) as $Val); - } - - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $ThemeSettingsCopyWith<$Res> get themeSettings { - return $ThemeSettingsCopyWith<$Res>(_value.themeSettings, (value) { - return _then(_value.copyWith(themeSettings: value) as $Val); - }); - } - - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $PlayerSettingsCopyWith<$Res> get playerSettings { - return $PlayerSettingsCopyWith<$Res>(_value.playerSettings, (value) { - return _then(_value.copyWith(playerSettings: value) as $Val); - }); - } - - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings { - return $SleepTimerSettingsCopyWith<$Res>(_value.sleepTimerSettings, - (value) { - return _then(_value.copyWith(sleepTimerSettings: value) as $Val); - }); - } - - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $DownloadSettingsCopyWith<$Res> get downloadSettings { - return $DownloadSettingsCopyWith<$Res>(_value.downloadSettings, (value) { - return _then(_value.copyWith(downloadSettings: value) as $Val); - }); - } - - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $NotificationSettingsCopyWith<$Res> get notificationSettings { - return $NotificationSettingsCopyWith<$Res>(_value.notificationSettings, - (value) { - return _then(_value.copyWith(notificationSettings: value) as $Val); - }); - } - - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $ShakeDetectionSettingsCopyWith<$Res> get shakeDetectionSettings { - return $ShakeDetectionSettingsCopyWith<$Res>(_value.shakeDetectionSettings, - (value) { - return _then(_value.copyWith(shakeDetectionSettings: value) as $Val); - }); - } +/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? themeSettings = null,Object? playerSettings = null,Object? sleepTimerSettings = null,Object? downloadSettings = null,Object? notificationSettings = null,Object? shakeDetectionSettings = null,Object? homePageSettings = null,}) { + return _then(_self.copyWith( +themeSettings: null == themeSettings ? _self.themeSettings : themeSettings // ignore: cast_nullable_to_non_nullable +as ThemeSettings,playerSettings: null == playerSettings ? _self.playerSettings : playerSettings // ignore: cast_nullable_to_non_nullable +as PlayerSettings,sleepTimerSettings: null == sleepTimerSettings ? _self.sleepTimerSettings : sleepTimerSettings // ignore: cast_nullable_to_non_nullable +as SleepTimerSettings,downloadSettings: null == downloadSettings ? _self.downloadSettings : downloadSettings // ignore: cast_nullable_to_non_nullable +as DownloadSettings,notificationSettings: null == notificationSettings ? _self.notificationSettings : notificationSettings // ignore: cast_nullable_to_non_nullable +as NotificationSettings,shakeDetectionSettings: null == shakeDetectionSettings ? _self.shakeDetectionSettings : shakeDetectionSettings // ignore: cast_nullable_to_non_nullable +as ShakeDetectionSettings,homePageSettings: null == homePageSettings ? _self.homePageSettings : homePageSettings // ignore: cast_nullable_to_non_nullable +as HomePageSettings, + )); +} +/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ThemeSettingsCopyWith<$Res> get themeSettings { + + return $ThemeSettingsCopyWith<$Res>(_self.themeSettings, (value) { + return _then(_self.copyWith(themeSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$PlayerSettingsCopyWith<$Res> get playerSettings { + + return $PlayerSettingsCopyWith<$Res>(_self.playerSettings, (value) { + return _then(_self.copyWith(playerSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings { + + return $SleepTimerSettingsCopyWith<$Res>(_self.sleepTimerSettings, (value) { + return _then(_self.copyWith(sleepTimerSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$DownloadSettingsCopyWith<$Res> get downloadSettings { + + return $DownloadSettingsCopyWith<$Res>(_self.downloadSettings, (value) { + return _then(_self.copyWith(downloadSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NotificationSettingsCopyWith<$Res> get notificationSettings { + + return $NotificationSettingsCopyWith<$Res>(_self.notificationSettings, (value) { + return _then(_self.copyWith(notificationSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ShakeDetectionSettingsCopyWith<$Res> get shakeDetectionSettings { + + return $ShakeDetectionSettingsCopyWith<$Res>(_self.shakeDetectionSettings, (value) { + return _then(_self.copyWith(shakeDetectionSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$HomePageSettingsCopyWith<$Res> get homePageSettings { + + return $HomePageSettingsCopyWith<$Res>(_self.homePageSettings, (value) { + return _then(_self.copyWith(homePageSettings: value)); + }); +} } -/// @nodoc -abstract class _$$AppSettingsImplCopyWith<$Res> - implements $AppSettingsCopyWith<$Res> { - factory _$$AppSettingsImplCopyWith( - _$AppSettingsImpl value, $Res Function(_$AppSettingsImpl) then) = - __$$AppSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {ThemeSettings themeSettings, - PlayerSettings playerSettings, - SleepTimerSettings sleepTimerSettings, - DownloadSettings downloadSettings, - NotificationSettings notificationSettings, - ShakeDetectionSettings shakeDetectionSettings}); - @override - $ThemeSettingsCopyWith<$Res> get themeSettings; - @override - $PlayerSettingsCopyWith<$Res> get playerSettings; - @override - $SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings; - @override - $DownloadSettingsCopyWith<$Res> get downloadSettings; - @override - $NotificationSettingsCopyWith<$Res> get notificationSettings; - @override - $ShakeDetectionSettingsCopyWith<$Res> get shakeDetectionSettings; +/// Adds pattern-matching-related methods to [AppSettings]. +extension AppSettingsPatterns on AppSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _AppSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _AppSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _AppSettings value) $default,){ +final _that = this; +switch (_that) { +case _AppSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _AppSettings value)? $default,){ +final _that = this; +switch (_that) { +case _AppSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( ThemeSettings themeSettings, PlayerSettings playerSettings, SleepTimerSettings sleepTimerSettings, DownloadSettings downloadSettings, NotificationSettings notificationSettings, ShakeDetectionSettings shakeDetectionSettings, HomePageSettings homePageSettings)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _AppSettings() when $default != null: +return $default(_that.themeSettings,_that.playerSettings,_that.sleepTimerSettings,_that.downloadSettings,_that.notificationSettings,_that.shakeDetectionSettings,_that.homePageSettings);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( ThemeSettings themeSettings, PlayerSettings playerSettings, SleepTimerSettings sleepTimerSettings, DownloadSettings downloadSettings, NotificationSettings notificationSettings, ShakeDetectionSettings shakeDetectionSettings, HomePageSettings homePageSettings) $default,) {final _that = this; +switch (_that) { +case _AppSettings(): +return $default(_that.themeSettings,_that.playerSettings,_that.sleepTimerSettings,_that.downloadSettings,_that.notificationSettings,_that.shakeDetectionSettings,_that.homePageSettings);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( ThemeSettings themeSettings, PlayerSettings playerSettings, SleepTimerSettings sleepTimerSettings, DownloadSettings downloadSettings, NotificationSettings notificationSettings, ShakeDetectionSettings shakeDetectionSettings, HomePageSettings homePageSettings)? $default,) {final _that = this; +switch (_that) { +case _AppSettings() when $default != null: +return $default(_that.themeSettings,_that.playerSettings,_that.sleepTimerSettings,_that.downloadSettings,_that.notificationSettings,_that.shakeDetectionSettings,_that.homePageSettings);case _: + return null; + +} } -/// @nodoc -class __$$AppSettingsImplCopyWithImpl<$Res> - extends _$AppSettingsCopyWithImpl<$Res, _$AppSettingsImpl> - implements _$$AppSettingsImplCopyWith<$Res> { - __$$AppSettingsImplCopyWithImpl( - _$AppSettingsImpl _value, $Res Function(_$AppSettingsImpl) _then) - : super(_value, _then); - - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? themeSettings = null, - Object? playerSettings = null, - Object? sleepTimerSettings = null, - Object? downloadSettings = null, - Object? notificationSettings = null, - Object? shakeDetectionSettings = null, - }) { - return _then(_$AppSettingsImpl( - themeSettings: null == themeSettings - ? _value.themeSettings - : themeSettings // ignore: cast_nullable_to_non_nullable - as ThemeSettings, - playerSettings: null == playerSettings - ? _value.playerSettings - : playerSettings // ignore: cast_nullable_to_non_nullable - as PlayerSettings, - sleepTimerSettings: null == sleepTimerSettings - ? _value.sleepTimerSettings - : sleepTimerSettings // ignore: cast_nullable_to_non_nullable - as SleepTimerSettings, - downloadSettings: null == downloadSettings - ? _value.downloadSettings - : downloadSettings // ignore: cast_nullable_to_non_nullable - as DownloadSettings, - notificationSettings: null == notificationSettings - ? _value.notificationSettings - : notificationSettings // ignore: cast_nullable_to_non_nullable - as NotificationSettings, - shakeDetectionSettings: null == shakeDetectionSettings - ? _value.shakeDetectionSettings - : shakeDetectionSettings // ignore: cast_nullable_to_non_nullable - as ShakeDetectionSettings, - )); - } } /// @nodoc @JsonSerializable() -class _$AppSettingsImpl implements _AppSettings { - const _$AppSettingsImpl( - {this.themeSettings = const ThemeSettings(), - this.playerSettings = const PlayerSettings(), - this.sleepTimerSettings = const SleepTimerSettings(), - this.downloadSettings = const DownloadSettings(), - this.notificationSettings = const NotificationSettings(), - this.shakeDetectionSettings = const ShakeDetectionSettings()}); - factory _$AppSettingsImpl.fromJson(Map json) => - _$$AppSettingsImplFromJson(json); +class _AppSettings implements AppSettings { + const _AppSettings({this.themeSettings = const ThemeSettings(), this.playerSettings = const PlayerSettings(), this.sleepTimerSettings = const SleepTimerSettings(), this.downloadSettings = const DownloadSettings(), this.notificationSettings = const NotificationSettings(), this.shakeDetectionSettings = const ShakeDetectionSettings(), this.homePageSettings = const HomePageSettings()}); + factory _AppSettings.fromJson(Map json) => _$AppSettingsFromJson(json); - @override - @JsonKey() - final ThemeSettings themeSettings; - @override - @JsonKey() - final PlayerSettings playerSettings; - @override - @JsonKey() - final SleepTimerSettings sleepTimerSettings; - @override - @JsonKey() - final DownloadSettings downloadSettings; - @override - @JsonKey() - final NotificationSettings notificationSettings; - @override - @JsonKey() - final ShakeDetectionSettings shakeDetectionSettings; +@override@JsonKey() final ThemeSettings themeSettings; +@override@JsonKey() final PlayerSettings playerSettings; +@override@JsonKey() final SleepTimerSettings sleepTimerSettings; +@override@JsonKey() final DownloadSettings downloadSettings; +@override@JsonKey() final NotificationSettings notificationSettings; +@override@JsonKey() final ShakeDetectionSettings shakeDetectionSettings; +@override@JsonKey() final HomePageSettings homePageSettings; - @override - String toString() { - return 'AppSettings(themeSettings: $themeSettings, playerSettings: $playerSettings, sleepTimerSettings: $sleepTimerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings, shakeDetectionSettings: $shakeDetectionSettings)'; - } +/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$AppSettingsCopyWith<_AppSettings> get copyWith => __$AppSettingsCopyWithImpl<_AppSettings>(this, _$identity); - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$AppSettingsImpl && - (identical(other.themeSettings, themeSettings) || - other.themeSettings == themeSettings) && - (identical(other.playerSettings, playerSettings) || - other.playerSettings == playerSettings) && - (identical(other.sleepTimerSettings, sleepTimerSettings) || - other.sleepTimerSettings == sleepTimerSettings) && - (identical(other.downloadSettings, downloadSettings) || - other.downloadSettings == downloadSettings) && - (identical(other.notificationSettings, notificationSettings) || - other.notificationSettings == notificationSettings) && - (identical(other.shakeDetectionSettings, shakeDetectionSettings) || - other.shakeDetectionSettings == shakeDetectionSettings)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - themeSettings, - playerSettings, - sleepTimerSettings, - downloadSettings, - notificationSettings, - shakeDetectionSettings); - - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$AppSettingsImplCopyWith<_$AppSettingsImpl> get copyWith => - __$$AppSettingsImplCopyWithImpl<_$AppSettingsImpl>(this, _$identity); - - @override - Map toJson() { - return _$$AppSettingsImplToJson( - this, - ); - } +@override +Map toJson() { + return _$AppSettingsToJson(this, ); } -abstract class _AppSettings implements AppSettings { - const factory _AppSettings( - {final ThemeSettings themeSettings, - final PlayerSettings playerSettings, - final SleepTimerSettings sleepTimerSettings, - final DownloadSettings downloadSettings, - final NotificationSettings notificationSettings, - final ShakeDetectionSettings shakeDetectionSettings}) = _$AppSettingsImpl; - - factory _AppSettings.fromJson(Map json) = - _$AppSettingsImpl.fromJson; - - @override - ThemeSettings get themeSettings; - @override - PlayerSettings get playerSettings; - @override - SleepTimerSettings get sleepTimerSettings; - @override - DownloadSettings get downloadSettings; - @override - NotificationSettings get notificationSettings; - @override - ShakeDetectionSettings get shakeDetectionSettings; - - /// Create a copy of AppSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$AppSettingsImplCopyWith<_$AppSettingsImpl> get copyWith => - throw _privateConstructorUsedError; +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.themeSettings, themeSettings) || other.themeSettings == themeSettings)&&(identical(other.playerSettings, playerSettings) || other.playerSettings == playerSettings)&&(identical(other.sleepTimerSettings, sleepTimerSettings) || other.sleepTimerSettings == sleepTimerSettings)&&(identical(other.downloadSettings, downloadSettings) || other.downloadSettings == downloadSettings)&&(identical(other.notificationSettings, notificationSettings) || other.notificationSettings == notificationSettings)&&(identical(other.shakeDetectionSettings, shakeDetectionSettings) || other.shakeDetectionSettings == shakeDetectionSettings)&&(identical(other.homePageSettings, homePageSettings) || other.homePageSettings == homePageSettings)); } -ThemeSettings _$ThemeSettingsFromJson(Map json) { - return _ThemeSettings.fromJson(json); +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,themeSettings,playerSettings,sleepTimerSettings,downloadSettings,notificationSettings,shakeDetectionSettings,homePageSettings); + +@override +String toString() { + return 'AppSettings(themeSettings: $themeSettings, playerSettings: $playerSettings, sleepTimerSettings: $sleepTimerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings, shakeDetectionSettings: $shakeDetectionSettings, homePageSettings: $homePageSettings)'; } + +} + +/// @nodoc +abstract mixin class _$AppSettingsCopyWith<$Res> implements $AppSettingsCopyWith<$Res> { + factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl; +@override @useResult +$Res call({ + ThemeSettings themeSettings, PlayerSettings playerSettings, SleepTimerSettings sleepTimerSettings, DownloadSettings downloadSettings, NotificationSettings notificationSettings, ShakeDetectionSettings shakeDetectionSettings, HomePageSettings homePageSettings +}); + + +@override $ThemeSettingsCopyWith<$Res> get themeSettings;@override $PlayerSettingsCopyWith<$Res> get playerSettings;@override $SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings;@override $DownloadSettingsCopyWith<$Res> get downloadSettings;@override $NotificationSettingsCopyWith<$Res> get notificationSettings;@override $ShakeDetectionSettingsCopyWith<$Res> get shakeDetectionSettings;@override $HomePageSettingsCopyWith<$Res> get homePageSettings; + +} +/// @nodoc +class __$AppSettingsCopyWithImpl<$Res> + implements _$AppSettingsCopyWith<$Res> { + __$AppSettingsCopyWithImpl(this._self, this._then); + + final _AppSettings _self; + final $Res Function(_AppSettings) _then; + +/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? themeSettings = null,Object? playerSettings = null,Object? sleepTimerSettings = null,Object? downloadSettings = null,Object? notificationSettings = null,Object? shakeDetectionSettings = null,Object? homePageSettings = null,}) { + return _then(_AppSettings( +themeSettings: null == themeSettings ? _self.themeSettings : themeSettings // ignore: cast_nullable_to_non_nullable +as ThemeSettings,playerSettings: null == playerSettings ? _self.playerSettings : playerSettings // ignore: cast_nullable_to_non_nullable +as PlayerSettings,sleepTimerSettings: null == sleepTimerSettings ? _self.sleepTimerSettings : sleepTimerSettings // ignore: cast_nullable_to_non_nullable +as SleepTimerSettings,downloadSettings: null == downloadSettings ? _self.downloadSettings : downloadSettings // ignore: cast_nullable_to_non_nullable +as DownloadSettings,notificationSettings: null == notificationSettings ? _self.notificationSettings : notificationSettings // ignore: cast_nullable_to_non_nullable +as NotificationSettings,shakeDetectionSettings: null == shakeDetectionSettings ? _self.shakeDetectionSettings : shakeDetectionSettings // ignore: cast_nullable_to_non_nullable +as ShakeDetectionSettings,homePageSettings: null == homePageSettings ? _self.homePageSettings : homePageSettings // ignore: cast_nullable_to_non_nullable +as HomePageSettings, + )); +} + +/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ThemeSettingsCopyWith<$Res> get themeSettings { + + return $ThemeSettingsCopyWith<$Res>(_self.themeSettings, (value) { + return _then(_self.copyWith(themeSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$PlayerSettingsCopyWith<$Res> get playerSettings { + + return $PlayerSettingsCopyWith<$Res>(_self.playerSettings, (value) { + return _then(_self.copyWith(playerSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings { + + return $SleepTimerSettingsCopyWith<$Res>(_self.sleepTimerSettings, (value) { + return _then(_self.copyWith(sleepTimerSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$DownloadSettingsCopyWith<$Res> get downloadSettings { + + return $DownloadSettingsCopyWith<$Res>(_self.downloadSettings, (value) { + return _then(_self.copyWith(downloadSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NotificationSettingsCopyWith<$Res> get notificationSettings { + + return $NotificationSettingsCopyWith<$Res>(_self.notificationSettings, (value) { + return _then(_self.copyWith(notificationSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ShakeDetectionSettingsCopyWith<$Res> get shakeDetectionSettings { + + return $ShakeDetectionSettingsCopyWith<$Res>(_self.shakeDetectionSettings, (value) { + return _then(_self.copyWith(shakeDetectionSettings: value)); + }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$HomePageSettingsCopyWith<$Res> get homePageSettings { + + return $HomePageSettingsCopyWith<$Res>(_self.homePageSettings, (value) { + return _then(_self.copyWith(homePageSettings: value)); + }); +} +} + + /// @nodoc mixin _$ThemeSettings { - bool get isDarkMode => throw _privateConstructorUsedError; - bool get useMaterialThemeOnItemPage => throw _privateConstructorUsedError; - bool get useCurrentPlayerThemeThroughoutApp => - throw _privateConstructorUsedError; + + ThemeMode get themeMode; bool get highContrast; bool get useMaterialThemeFromSystem; String get customThemeColor; bool get useMaterialThemeOnItemPage; bool get useCurrentPlayerThemeThroughoutApp; +/// Create a copy of ThemeSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ThemeSettingsCopyWith get copyWith => _$ThemeSettingsCopyWithImpl(this as ThemeSettings, _$identity); /// Serializes this ThemeSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ThemeSettings&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.highContrast, highContrast) || other.highContrast == highContrast)&&(identical(other.useMaterialThemeFromSystem, useMaterialThemeFromSystem) || other.useMaterialThemeFromSystem == useMaterialThemeFromSystem)&&(identical(other.customThemeColor, customThemeColor) || other.customThemeColor == customThemeColor)&&(identical(other.useMaterialThemeOnItemPage, useMaterialThemeOnItemPage) || other.useMaterialThemeOnItemPage == useMaterialThemeOnItemPage)&&(identical(other.useCurrentPlayerThemeThroughoutApp, useCurrentPlayerThemeThroughoutApp) || other.useCurrentPlayerThemeThroughoutApp == useCurrentPlayerThemeThroughoutApp)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,themeMode,highContrast,useMaterialThemeFromSystem,customThemeColor,useMaterialThemeOnItemPage,useCurrentPlayerThemeThroughoutApp); + +@override +String toString() { + return 'ThemeSettings(themeMode: $themeMode, highContrast: $highContrast, useMaterialThemeFromSystem: $useMaterialThemeFromSystem, customThemeColor: $customThemeColor, useMaterialThemeOnItemPage: $useMaterialThemeOnItemPage, useCurrentPlayerThemeThroughoutApp: $useCurrentPlayerThemeThroughoutApp)'; +} + - /// Create a copy of ThemeSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ThemeSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $ThemeSettingsCopyWith<$Res> { - factory $ThemeSettingsCopyWith( - ThemeSettings value, $Res Function(ThemeSettings) then) = - _$ThemeSettingsCopyWithImpl<$Res, ThemeSettings>; - @useResult - $Res call( - {bool isDarkMode, - bool useMaterialThemeOnItemPage, - bool useCurrentPlayerThemeThroughoutApp}); -} +abstract mixin class $ThemeSettingsCopyWith<$Res> { + factory $ThemeSettingsCopyWith(ThemeSettings value, $Res Function(ThemeSettings) _then) = _$ThemeSettingsCopyWithImpl; +@useResult +$Res call({ + ThemeMode themeMode, bool highContrast, bool useMaterialThemeFromSystem, String customThemeColor, bool useMaterialThemeOnItemPage, bool useCurrentPlayerThemeThroughoutApp +}); + + + +} /// @nodoc -class _$ThemeSettingsCopyWithImpl<$Res, $Val extends ThemeSettings> +class _$ThemeSettingsCopyWithImpl<$Res> implements $ThemeSettingsCopyWith<$Res> { - _$ThemeSettingsCopyWithImpl(this._value, this._then); + _$ThemeSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final ThemeSettings _self; + final $Res Function(ThemeSettings) _then; - /// Create a copy of ThemeSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? isDarkMode = null, - Object? useMaterialThemeOnItemPage = null, - Object? useCurrentPlayerThemeThroughoutApp = null, - }) { - return _then(_value.copyWith( - isDarkMode: null == isDarkMode - ? _value.isDarkMode - : isDarkMode // ignore: cast_nullable_to_non_nullable - as bool, - useMaterialThemeOnItemPage: null == useMaterialThemeOnItemPage - ? _value.useMaterialThemeOnItemPage - : useMaterialThemeOnItemPage // ignore: cast_nullable_to_non_nullable - as bool, - useCurrentPlayerThemeThroughoutApp: null == - useCurrentPlayerThemeThroughoutApp - ? _value.useCurrentPlayerThemeThroughoutApp - : useCurrentPlayerThemeThroughoutApp // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } +/// Create a copy of ThemeSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? themeMode = null,Object? highContrast = null,Object? useMaterialThemeFromSystem = null,Object? customThemeColor = null,Object? useMaterialThemeOnItemPage = null,Object? useCurrentPlayerThemeThroughoutApp = null,}) { + return _then(_self.copyWith( +themeMode: null == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable +as ThemeMode,highContrast: null == highContrast ? _self.highContrast : highContrast // ignore: cast_nullable_to_non_nullable +as bool,useMaterialThemeFromSystem: null == useMaterialThemeFromSystem ? _self.useMaterialThemeFromSystem : useMaterialThemeFromSystem // ignore: cast_nullable_to_non_nullable +as bool,customThemeColor: null == customThemeColor ? _self.customThemeColor : customThemeColor // ignore: cast_nullable_to_non_nullable +as String,useMaterialThemeOnItemPage: null == useMaterialThemeOnItemPage ? _self.useMaterialThemeOnItemPage : useMaterialThemeOnItemPage // ignore: cast_nullable_to_non_nullable +as bool,useCurrentPlayerThemeThroughoutApp: null == useCurrentPlayerThemeThroughoutApp ? _self.useCurrentPlayerThemeThroughoutApp : useCurrentPlayerThemeThroughoutApp // ignore: cast_nullable_to_non_nullable +as bool, + )); } -/// @nodoc -abstract class _$$ThemeSettingsImplCopyWith<$Res> - implements $ThemeSettingsCopyWith<$Res> { - factory _$$ThemeSettingsImplCopyWith( - _$ThemeSettingsImpl value, $Res Function(_$ThemeSettingsImpl) then) = - __$$ThemeSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {bool isDarkMode, - bool useMaterialThemeOnItemPage, - bool useCurrentPlayerThemeThroughoutApp}); } -/// @nodoc -class __$$ThemeSettingsImplCopyWithImpl<$Res> - extends _$ThemeSettingsCopyWithImpl<$Res, _$ThemeSettingsImpl> - implements _$$ThemeSettingsImplCopyWith<$Res> { - __$$ThemeSettingsImplCopyWithImpl( - _$ThemeSettingsImpl _value, $Res Function(_$ThemeSettingsImpl) _then) - : super(_value, _then); - /// Create a copy of ThemeSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? isDarkMode = null, - Object? useMaterialThemeOnItemPage = null, - Object? useCurrentPlayerThemeThroughoutApp = null, - }) { - return _then(_$ThemeSettingsImpl( - isDarkMode: null == isDarkMode - ? _value.isDarkMode - : isDarkMode // ignore: cast_nullable_to_non_nullable - as bool, - useMaterialThemeOnItemPage: null == useMaterialThemeOnItemPage - ? _value.useMaterialThemeOnItemPage - : useMaterialThemeOnItemPage // ignore: cast_nullable_to_non_nullable - as bool, - useCurrentPlayerThemeThroughoutApp: null == - useCurrentPlayerThemeThroughoutApp - ? _value.useCurrentPlayerThemeThroughoutApp - : useCurrentPlayerThemeThroughoutApp // ignore: cast_nullable_to_non_nullable - as bool, - )); - } +/// Adds pattern-matching-related methods to [ThemeSettings]. +extension ThemeSettingsPatterns on ThemeSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ThemeSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ThemeSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ThemeSettings value) $default,){ +final _that = this; +switch (_that) { +case _ThemeSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ThemeSettings value)? $default,){ +final _that = this; +switch (_that) { +case _ThemeSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( ThemeMode themeMode, bool highContrast, bool useMaterialThemeFromSystem, String customThemeColor, bool useMaterialThemeOnItemPage, bool useCurrentPlayerThemeThroughoutApp)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ThemeSettings() when $default != null: +return $default(_that.themeMode,_that.highContrast,_that.useMaterialThemeFromSystem,_that.customThemeColor,_that.useMaterialThemeOnItemPage,_that.useCurrentPlayerThemeThroughoutApp);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( ThemeMode themeMode, bool highContrast, bool useMaterialThemeFromSystem, String customThemeColor, bool useMaterialThemeOnItemPage, bool useCurrentPlayerThemeThroughoutApp) $default,) {final _that = this; +switch (_that) { +case _ThemeSettings(): +return $default(_that.themeMode,_that.highContrast,_that.useMaterialThemeFromSystem,_that.customThemeColor,_that.useMaterialThemeOnItemPage,_that.useCurrentPlayerThemeThroughoutApp);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( ThemeMode themeMode, bool highContrast, bool useMaterialThemeFromSystem, String customThemeColor, bool useMaterialThemeOnItemPage, bool useCurrentPlayerThemeThroughoutApp)? $default,) {final _that = this; +switch (_that) { +case _ThemeSettings() when $default != null: +return $default(_that.themeMode,_that.highContrast,_that.useMaterialThemeFromSystem,_that.customThemeColor,_that.useMaterialThemeOnItemPage,_that.useCurrentPlayerThemeThroughoutApp);case _: + return null; + +} +} + } /// @nodoc @JsonSerializable() -class _$ThemeSettingsImpl implements _ThemeSettings { - const _$ThemeSettingsImpl( - {this.isDarkMode = true, - this.useMaterialThemeOnItemPage = true, - this.useCurrentPlayerThemeThroughoutApp = true}); - factory _$ThemeSettingsImpl.fromJson(Map json) => - _$$ThemeSettingsImplFromJson(json); +class _ThemeSettings implements ThemeSettings { + const _ThemeSettings({this.themeMode = ThemeMode.system, this.highContrast = false, this.useMaterialThemeFromSystem = false, this.customThemeColor = '#FF311B92', this.useMaterialThemeOnItemPage = true, this.useCurrentPlayerThemeThroughoutApp = true}); + factory _ThemeSettings.fromJson(Map json) => _$ThemeSettingsFromJson(json); - @override - @JsonKey() - final bool isDarkMode; - @override - @JsonKey() - final bool useMaterialThemeOnItemPage; - @override - @JsonKey() - final bool useCurrentPlayerThemeThroughoutApp; +@override@JsonKey() final ThemeMode themeMode; +@override@JsonKey() final bool highContrast; +@override@JsonKey() final bool useMaterialThemeFromSystem; +@override@JsonKey() final String customThemeColor; +@override@JsonKey() final bool useMaterialThemeOnItemPage; +@override@JsonKey() final bool useCurrentPlayerThemeThroughoutApp; - @override - String toString() { - return 'ThemeSettings(isDarkMode: $isDarkMode, useMaterialThemeOnItemPage: $useMaterialThemeOnItemPage, useCurrentPlayerThemeThroughoutApp: $useCurrentPlayerThemeThroughoutApp)'; - } +/// Create a copy of ThemeSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ThemeSettingsCopyWith<_ThemeSettings> get copyWith => __$ThemeSettingsCopyWithImpl<_ThemeSettings>(this, _$identity); - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ThemeSettingsImpl && - (identical(other.isDarkMode, isDarkMode) || - other.isDarkMode == isDarkMode) && - (identical(other.useMaterialThemeOnItemPage, - useMaterialThemeOnItemPage) || - other.useMaterialThemeOnItemPage == - useMaterialThemeOnItemPage) && - (identical(other.useCurrentPlayerThemeThroughoutApp, - useCurrentPlayerThemeThroughoutApp) || - other.useCurrentPlayerThemeThroughoutApp == - useCurrentPlayerThemeThroughoutApp)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, isDarkMode, - useMaterialThemeOnItemPage, useCurrentPlayerThemeThroughoutApp); - - /// Create a copy of ThemeSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ThemeSettingsImplCopyWith<_$ThemeSettingsImpl> get copyWith => - __$$ThemeSettingsImplCopyWithImpl<_$ThemeSettingsImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ThemeSettingsImplToJson( - this, - ); - } +@override +Map toJson() { + return _$ThemeSettingsToJson(this, ); } -abstract class _ThemeSettings implements ThemeSettings { - const factory _ThemeSettings( - {final bool isDarkMode, - final bool useMaterialThemeOnItemPage, - final bool useCurrentPlayerThemeThroughoutApp}) = _$ThemeSettingsImpl; - - factory _ThemeSettings.fromJson(Map json) = - _$ThemeSettingsImpl.fromJson; - - @override - bool get isDarkMode; - @override - bool get useMaterialThemeOnItemPage; - @override - bool get useCurrentPlayerThemeThroughoutApp; - - /// Create a copy of ThemeSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ThemeSettingsImplCopyWith<_$ThemeSettingsImpl> get copyWith => - throw _privateConstructorUsedError; +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ThemeSettings&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.highContrast, highContrast) || other.highContrast == highContrast)&&(identical(other.useMaterialThemeFromSystem, useMaterialThemeFromSystem) || other.useMaterialThemeFromSystem == useMaterialThemeFromSystem)&&(identical(other.customThemeColor, customThemeColor) || other.customThemeColor == customThemeColor)&&(identical(other.useMaterialThemeOnItemPage, useMaterialThemeOnItemPage) || other.useMaterialThemeOnItemPage == useMaterialThemeOnItemPage)&&(identical(other.useCurrentPlayerThemeThroughoutApp, useCurrentPlayerThemeThroughoutApp) || other.useCurrentPlayerThemeThroughoutApp == useCurrentPlayerThemeThroughoutApp)); } -PlayerSettings _$PlayerSettingsFromJson(Map json) { - return _PlayerSettings.fromJson(json); +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,themeMode,highContrast,useMaterialThemeFromSystem,customThemeColor,useMaterialThemeOnItemPage,useCurrentPlayerThemeThroughoutApp); + +@override +String toString() { + return 'ThemeSettings(themeMode: $themeMode, highContrast: $highContrast, useMaterialThemeFromSystem: $useMaterialThemeFromSystem, customThemeColor: $customThemeColor, useMaterialThemeOnItemPage: $useMaterialThemeOnItemPage, useCurrentPlayerThemeThroughoutApp: $useCurrentPlayerThemeThroughoutApp)'; } + +} + +/// @nodoc +abstract mixin class _$ThemeSettingsCopyWith<$Res> implements $ThemeSettingsCopyWith<$Res> { + factory _$ThemeSettingsCopyWith(_ThemeSettings value, $Res Function(_ThemeSettings) _then) = __$ThemeSettingsCopyWithImpl; +@override @useResult +$Res call({ + ThemeMode themeMode, bool highContrast, bool useMaterialThemeFromSystem, String customThemeColor, bool useMaterialThemeOnItemPage, bool useCurrentPlayerThemeThroughoutApp +}); + + + + +} +/// @nodoc +class __$ThemeSettingsCopyWithImpl<$Res> + implements _$ThemeSettingsCopyWith<$Res> { + __$ThemeSettingsCopyWithImpl(this._self, this._then); + + final _ThemeSettings _self; + final $Res Function(_ThemeSettings) _then; + +/// Create a copy of ThemeSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? themeMode = null,Object? highContrast = null,Object? useMaterialThemeFromSystem = null,Object? customThemeColor = null,Object? useMaterialThemeOnItemPage = null,Object? useCurrentPlayerThemeThroughoutApp = null,}) { + return _then(_ThemeSettings( +themeMode: null == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable +as ThemeMode,highContrast: null == highContrast ? _self.highContrast : highContrast // ignore: cast_nullable_to_non_nullable +as bool,useMaterialThemeFromSystem: null == useMaterialThemeFromSystem ? _self.useMaterialThemeFromSystem : useMaterialThemeFromSystem // ignore: cast_nullable_to_non_nullable +as bool,customThemeColor: null == customThemeColor ? _self.customThemeColor : customThemeColor // ignore: cast_nullable_to_non_nullable +as String,useMaterialThemeOnItemPage: null == useMaterialThemeOnItemPage ? _self.useMaterialThemeOnItemPage : useMaterialThemeOnItemPage // ignore: cast_nullable_to_non_nullable +as bool,useCurrentPlayerThemeThroughoutApp: null == useCurrentPlayerThemeThroughoutApp ? _self.useCurrentPlayerThemeThroughoutApp : useCurrentPlayerThemeThroughoutApp // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + + +} + + /// @nodoc mixin _$PlayerSettings { - MinimizedPlayerSettings get miniPlayerSettings => - throw _privateConstructorUsedError; - ExpandedPlayerSettings get expandedPlayerSettings => - throw _privateConstructorUsedError; - double get preferredDefaultVolume => throw _privateConstructorUsedError; - double get preferredDefaultSpeed => throw _privateConstructorUsedError; - List get speedOptions => throw _privateConstructorUsedError; - double get speedIncrement => throw _privateConstructorUsedError; - double get minSpeed => throw _privateConstructorUsedError; - double get maxSpeed => throw _privateConstructorUsedError; - Duration get minimumPositionForReporting => - throw _privateConstructorUsedError; - Duration get playbackReportInterval => throw _privateConstructorUsedError; - Duration get markCompleteWhenTimeLeft => throw _privateConstructorUsedError; - bool get configurePlayerForEveryBook => throw _privateConstructorUsedError; + + MinimizedPlayerSettings get miniPlayerSettings; ExpandedPlayerSettings get expandedPlayerSettings; double get preferredDefaultVolume; double get preferredDefaultSpeed; List get speedOptions; double get speedIncrement; double get minSpeed; double get maxSpeed; Duration get minimumPositionForReporting; Duration get playbackReportInterval; Duration get markCompleteWhenTimeLeft; bool get configurePlayerForEveryBook; +/// Create a copy of PlayerSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$PlayerSettingsCopyWith get copyWith => _$PlayerSettingsCopyWithImpl(this as PlayerSettings, _$identity); /// Serializes this PlayerSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is PlayerSettings&&(identical(other.miniPlayerSettings, miniPlayerSettings) || other.miniPlayerSettings == miniPlayerSettings)&&(identical(other.expandedPlayerSettings, expandedPlayerSettings) || other.expandedPlayerSettings == expandedPlayerSettings)&&(identical(other.preferredDefaultVolume, preferredDefaultVolume) || other.preferredDefaultVolume == preferredDefaultVolume)&&(identical(other.preferredDefaultSpeed, preferredDefaultSpeed) || other.preferredDefaultSpeed == preferredDefaultSpeed)&&const DeepCollectionEquality().equals(other.speedOptions, speedOptions)&&(identical(other.speedIncrement, speedIncrement) || other.speedIncrement == speedIncrement)&&(identical(other.minSpeed, minSpeed) || other.minSpeed == minSpeed)&&(identical(other.maxSpeed, maxSpeed) || other.maxSpeed == maxSpeed)&&(identical(other.minimumPositionForReporting, minimumPositionForReporting) || other.minimumPositionForReporting == minimumPositionForReporting)&&(identical(other.playbackReportInterval, playbackReportInterval) || other.playbackReportInterval == playbackReportInterval)&&(identical(other.markCompleteWhenTimeLeft, markCompleteWhenTimeLeft) || other.markCompleteWhenTimeLeft == markCompleteWhenTimeLeft)&&(identical(other.configurePlayerForEveryBook, configurePlayerForEveryBook) || other.configurePlayerForEveryBook == configurePlayerForEveryBook)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,miniPlayerSettings,expandedPlayerSettings,preferredDefaultVolume,preferredDefaultSpeed,const DeepCollectionEquality().hash(speedOptions),speedIncrement,minSpeed,maxSpeed,minimumPositionForReporting,playbackReportInterval,markCompleteWhenTimeLeft,configurePlayerForEveryBook); + +@override +String toString() { + return 'PlayerSettings(miniPlayerSettings: $miniPlayerSettings, expandedPlayerSettings: $expandedPlayerSettings, preferredDefaultVolume: $preferredDefaultVolume, preferredDefaultSpeed: $preferredDefaultSpeed, speedOptions: $speedOptions, speedIncrement: $speedIncrement, minSpeed: $minSpeed, maxSpeed: $maxSpeed, minimumPositionForReporting: $minimumPositionForReporting, playbackReportInterval: $playbackReportInterval, markCompleteWhenTimeLeft: $markCompleteWhenTimeLeft, configurePlayerForEveryBook: $configurePlayerForEveryBook)'; +} + - /// Create a copy of PlayerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $PlayerSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $PlayerSettingsCopyWith<$Res> { - factory $PlayerSettingsCopyWith( - PlayerSettings value, $Res Function(PlayerSettings) then) = - _$PlayerSettingsCopyWithImpl<$Res, PlayerSettings>; - @useResult - $Res call( - {MinimizedPlayerSettings miniPlayerSettings, - ExpandedPlayerSettings expandedPlayerSettings, - double preferredDefaultVolume, - double preferredDefaultSpeed, - List speedOptions, - double speedIncrement, - double minSpeed, - double maxSpeed, - Duration minimumPositionForReporting, - Duration playbackReportInterval, - Duration markCompleteWhenTimeLeft, - bool configurePlayerForEveryBook}); +abstract mixin class $PlayerSettingsCopyWith<$Res> { + factory $PlayerSettingsCopyWith(PlayerSettings value, $Res Function(PlayerSettings) _then) = _$PlayerSettingsCopyWithImpl; +@useResult +$Res call({ + MinimizedPlayerSettings miniPlayerSettings, ExpandedPlayerSettings expandedPlayerSettings, double preferredDefaultVolume, double preferredDefaultSpeed, List speedOptions, double speedIncrement, double minSpeed, double maxSpeed, Duration minimumPositionForReporting, Duration playbackReportInterval, Duration markCompleteWhenTimeLeft, bool configurePlayerForEveryBook +}); + + +$MinimizedPlayerSettingsCopyWith<$Res> get miniPlayerSettings;$ExpandedPlayerSettingsCopyWith<$Res> get expandedPlayerSettings; - $MinimizedPlayerSettingsCopyWith<$Res> get miniPlayerSettings; - $ExpandedPlayerSettingsCopyWith<$Res> get expandedPlayerSettings; } - /// @nodoc -class _$PlayerSettingsCopyWithImpl<$Res, $Val extends PlayerSettings> +class _$PlayerSettingsCopyWithImpl<$Res> implements $PlayerSettingsCopyWith<$Res> { - _$PlayerSettingsCopyWithImpl(this._value, this._then); + _$PlayerSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final PlayerSettings _self; + final $Res Function(PlayerSettings) _then; - /// Create a copy of PlayerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? miniPlayerSettings = null, - Object? expandedPlayerSettings = null, - Object? preferredDefaultVolume = null, - Object? preferredDefaultSpeed = null, - Object? speedOptions = null, - Object? speedIncrement = null, - Object? minSpeed = null, - Object? maxSpeed = null, - Object? minimumPositionForReporting = null, - Object? playbackReportInterval = null, - Object? markCompleteWhenTimeLeft = null, - Object? configurePlayerForEveryBook = null, - }) { - return _then(_value.copyWith( - miniPlayerSettings: null == miniPlayerSettings - ? _value.miniPlayerSettings - : miniPlayerSettings // ignore: cast_nullable_to_non_nullable - as MinimizedPlayerSettings, - expandedPlayerSettings: null == expandedPlayerSettings - ? _value.expandedPlayerSettings - : expandedPlayerSettings // ignore: cast_nullable_to_non_nullable - as ExpandedPlayerSettings, - preferredDefaultVolume: null == preferredDefaultVolume - ? _value.preferredDefaultVolume - : preferredDefaultVolume // ignore: cast_nullable_to_non_nullable - as double, - preferredDefaultSpeed: null == preferredDefaultSpeed - ? _value.preferredDefaultSpeed - : preferredDefaultSpeed // ignore: cast_nullable_to_non_nullable - as double, - speedOptions: null == speedOptions - ? _value.speedOptions - : speedOptions // ignore: cast_nullable_to_non_nullable - as List, - speedIncrement: null == speedIncrement - ? _value.speedIncrement - : speedIncrement // ignore: cast_nullable_to_non_nullable - as double, - minSpeed: null == minSpeed - ? _value.minSpeed - : minSpeed // ignore: cast_nullable_to_non_nullable - as double, - maxSpeed: null == maxSpeed - ? _value.maxSpeed - : maxSpeed // ignore: cast_nullable_to_non_nullable - as double, - minimumPositionForReporting: null == minimumPositionForReporting - ? _value.minimumPositionForReporting - : minimumPositionForReporting // ignore: cast_nullable_to_non_nullable - as Duration, - playbackReportInterval: null == playbackReportInterval - ? _value.playbackReportInterval - : playbackReportInterval // ignore: cast_nullable_to_non_nullable - as Duration, - markCompleteWhenTimeLeft: null == markCompleteWhenTimeLeft - ? _value.markCompleteWhenTimeLeft - : markCompleteWhenTimeLeft // ignore: cast_nullable_to_non_nullable - as Duration, - configurePlayerForEveryBook: null == configurePlayerForEveryBook - ? _value.configurePlayerForEveryBook - : configurePlayerForEveryBook // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } - - /// Create a copy of PlayerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $MinimizedPlayerSettingsCopyWith<$Res> get miniPlayerSettings { - return $MinimizedPlayerSettingsCopyWith<$Res>(_value.miniPlayerSettings, - (value) { - return _then(_value.copyWith(miniPlayerSettings: value) as $Val); - }); - } - - /// Create a copy of PlayerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $ExpandedPlayerSettingsCopyWith<$Res> get expandedPlayerSettings { - return $ExpandedPlayerSettingsCopyWith<$Res>(_value.expandedPlayerSettings, - (value) { - return _then(_value.copyWith(expandedPlayerSettings: value) as $Val); - }); - } +/// Create a copy of PlayerSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? miniPlayerSettings = null,Object? expandedPlayerSettings = null,Object? preferredDefaultVolume = null,Object? preferredDefaultSpeed = null,Object? speedOptions = null,Object? speedIncrement = null,Object? minSpeed = null,Object? maxSpeed = null,Object? minimumPositionForReporting = null,Object? playbackReportInterval = null,Object? markCompleteWhenTimeLeft = null,Object? configurePlayerForEveryBook = null,}) { + return _then(_self.copyWith( +miniPlayerSettings: null == miniPlayerSettings ? _self.miniPlayerSettings : miniPlayerSettings // ignore: cast_nullable_to_non_nullable +as MinimizedPlayerSettings,expandedPlayerSettings: null == expandedPlayerSettings ? _self.expandedPlayerSettings : expandedPlayerSettings // ignore: cast_nullable_to_non_nullable +as ExpandedPlayerSettings,preferredDefaultVolume: null == preferredDefaultVolume ? _self.preferredDefaultVolume : preferredDefaultVolume // ignore: cast_nullable_to_non_nullable +as double,preferredDefaultSpeed: null == preferredDefaultSpeed ? _self.preferredDefaultSpeed : preferredDefaultSpeed // ignore: cast_nullable_to_non_nullable +as double,speedOptions: null == speedOptions ? _self.speedOptions : speedOptions // ignore: cast_nullable_to_non_nullable +as List,speedIncrement: null == speedIncrement ? _self.speedIncrement : speedIncrement // ignore: cast_nullable_to_non_nullable +as double,minSpeed: null == minSpeed ? _self.minSpeed : minSpeed // ignore: cast_nullable_to_non_nullable +as double,maxSpeed: null == maxSpeed ? _self.maxSpeed : maxSpeed // ignore: cast_nullable_to_non_nullable +as double,minimumPositionForReporting: null == minimumPositionForReporting ? _self.minimumPositionForReporting : minimumPositionForReporting // ignore: cast_nullable_to_non_nullable +as Duration,playbackReportInterval: null == playbackReportInterval ? _self.playbackReportInterval : playbackReportInterval // ignore: cast_nullable_to_non_nullable +as Duration,markCompleteWhenTimeLeft: null == markCompleteWhenTimeLeft ? _self.markCompleteWhenTimeLeft : markCompleteWhenTimeLeft // ignore: cast_nullable_to_non_nullable +as Duration,configurePlayerForEveryBook: null == configurePlayerForEveryBook ? _self.configurePlayerForEveryBook : configurePlayerForEveryBook // ignore: cast_nullable_to_non_nullable +as bool, + )); +} +/// Create a copy of PlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$MinimizedPlayerSettingsCopyWith<$Res> get miniPlayerSettings { + + return $MinimizedPlayerSettingsCopyWith<$Res>(_self.miniPlayerSettings, (value) { + return _then(_self.copyWith(miniPlayerSettings: value)); + }); +}/// Create a copy of PlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ExpandedPlayerSettingsCopyWith<$Res> get expandedPlayerSettings { + + return $ExpandedPlayerSettingsCopyWith<$Res>(_self.expandedPlayerSettings, (value) { + return _then(_self.copyWith(expandedPlayerSettings: value)); + }); +} } -/// @nodoc -abstract class _$$PlayerSettingsImplCopyWith<$Res> - implements $PlayerSettingsCopyWith<$Res> { - factory _$$PlayerSettingsImplCopyWith(_$PlayerSettingsImpl value, - $Res Function(_$PlayerSettingsImpl) then) = - __$$PlayerSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {MinimizedPlayerSettings miniPlayerSettings, - ExpandedPlayerSettings expandedPlayerSettings, - double preferredDefaultVolume, - double preferredDefaultSpeed, - List speedOptions, - double speedIncrement, - double minSpeed, - double maxSpeed, - Duration minimumPositionForReporting, - Duration playbackReportInterval, - Duration markCompleteWhenTimeLeft, - bool configurePlayerForEveryBook}); - @override - $MinimizedPlayerSettingsCopyWith<$Res> get miniPlayerSettings; - @override - $ExpandedPlayerSettingsCopyWith<$Res> get expandedPlayerSettings; +/// Adds pattern-matching-related methods to [PlayerSettings]. +extension PlayerSettingsPatterns on PlayerSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _PlayerSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _PlayerSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _PlayerSettings value) $default,){ +final _that = this; +switch (_that) { +case _PlayerSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _PlayerSettings value)? $default,){ +final _that = this; +switch (_that) { +case _PlayerSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( MinimizedPlayerSettings miniPlayerSettings, ExpandedPlayerSettings expandedPlayerSettings, double preferredDefaultVolume, double preferredDefaultSpeed, List speedOptions, double speedIncrement, double minSpeed, double maxSpeed, Duration minimumPositionForReporting, Duration playbackReportInterval, Duration markCompleteWhenTimeLeft, bool configurePlayerForEveryBook)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _PlayerSettings() when $default != null: +return $default(_that.miniPlayerSettings,_that.expandedPlayerSettings,_that.preferredDefaultVolume,_that.preferredDefaultSpeed,_that.speedOptions,_that.speedIncrement,_that.minSpeed,_that.maxSpeed,_that.minimumPositionForReporting,_that.playbackReportInterval,_that.markCompleteWhenTimeLeft,_that.configurePlayerForEveryBook);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( MinimizedPlayerSettings miniPlayerSettings, ExpandedPlayerSettings expandedPlayerSettings, double preferredDefaultVolume, double preferredDefaultSpeed, List speedOptions, double speedIncrement, double minSpeed, double maxSpeed, Duration minimumPositionForReporting, Duration playbackReportInterval, Duration markCompleteWhenTimeLeft, bool configurePlayerForEveryBook) $default,) {final _that = this; +switch (_that) { +case _PlayerSettings(): +return $default(_that.miniPlayerSettings,_that.expandedPlayerSettings,_that.preferredDefaultVolume,_that.preferredDefaultSpeed,_that.speedOptions,_that.speedIncrement,_that.minSpeed,_that.maxSpeed,_that.minimumPositionForReporting,_that.playbackReportInterval,_that.markCompleteWhenTimeLeft,_that.configurePlayerForEveryBook);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( MinimizedPlayerSettings miniPlayerSettings, ExpandedPlayerSettings expandedPlayerSettings, double preferredDefaultVolume, double preferredDefaultSpeed, List speedOptions, double speedIncrement, double minSpeed, double maxSpeed, Duration minimumPositionForReporting, Duration playbackReportInterval, Duration markCompleteWhenTimeLeft, bool configurePlayerForEveryBook)? $default,) {final _that = this; +switch (_that) { +case _PlayerSettings() when $default != null: +return $default(_that.miniPlayerSettings,_that.expandedPlayerSettings,_that.preferredDefaultVolume,_that.preferredDefaultSpeed,_that.speedOptions,_that.speedIncrement,_that.minSpeed,_that.maxSpeed,_that.minimumPositionForReporting,_that.playbackReportInterval,_that.markCompleteWhenTimeLeft,_that.configurePlayerForEveryBook);case _: + return null; + +} } -/// @nodoc -class __$$PlayerSettingsImplCopyWithImpl<$Res> - extends _$PlayerSettingsCopyWithImpl<$Res, _$PlayerSettingsImpl> - implements _$$PlayerSettingsImplCopyWith<$Res> { - __$$PlayerSettingsImplCopyWithImpl( - _$PlayerSettingsImpl _value, $Res Function(_$PlayerSettingsImpl) _then) - : super(_value, _then); - - /// Create a copy of PlayerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? miniPlayerSettings = null, - Object? expandedPlayerSettings = null, - Object? preferredDefaultVolume = null, - Object? preferredDefaultSpeed = null, - Object? speedOptions = null, - Object? speedIncrement = null, - Object? minSpeed = null, - Object? maxSpeed = null, - Object? minimumPositionForReporting = null, - Object? playbackReportInterval = null, - Object? markCompleteWhenTimeLeft = null, - Object? configurePlayerForEveryBook = null, - }) { - return _then(_$PlayerSettingsImpl( - miniPlayerSettings: null == miniPlayerSettings - ? _value.miniPlayerSettings - : miniPlayerSettings // ignore: cast_nullable_to_non_nullable - as MinimizedPlayerSettings, - expandedPlayerSettings: null == expandedPlayerSettings - ? _value.expandedPlayerSettings - : expandedPlayerSettings // ignore: cast_nullable_to_non_nullable - as ExpandedPlayerSettings, - preferredDefaultVolume: null == preferredDefaultVolume - ? _value.preferredDefaultVolume - : preferredDefaultVolume // ignore: cast_nullable_to_non_nullable - as double, - preferredDefaultSpeed: null == preferredDefaultSpeed - ? _value.preferredDefaultSpeed - : preferredDefaultSpeed // ignore: cast_nullable_to_non_nullable - as double, - speedOptions: null == speedOptions - ? _value._speedOptions - : speedOptions // ignore: cast_nullable_to_non_nullable - as List, - speedIncrement: null == speedIncrement - ? _value.speedIncrement - : speedIncrement // ignore: cast_nullable_to_non_nullable - as double, - minSpeed: null == minSpeed - ? _value.minSpeed - : minSpeed // ignore: cast_nullable_to_non_nullable - as double, - maxSpeed: null == maxSpeed - ? _value.maxSpeed - : maxSpeed // ignore: cast_nullable_to_non_nullable - as double, - minimumPositionForReporting: null == minimumPositionForReporting - ? _value.minimumPositionForReporting - : minimumPositionForReporting // ignore: cast_nullable_to_non_nullable - as Duration, - playbackReportInterval: null == playbackReportInterval - ? _value.playbackReportInterval - : playbackReportInterval // ignore: cast_nullable_to_non_nullable - as Duration, - markCompleteWhenTimeLeft: null == markCompleteWhenTimeLeft - ? _value.markCompleteWhenTimeLeft - : markCompleteWhenTimeLeft // ignore: cast_nullable_to_non_nullable - as Duration, - configurePlayerForEveryBook: null == configurePlayerForEveryBook - ? _value.configurePlayerForEveryBook - : configurePlayerForEveryBook // ignore: cast_nullable_to_non_nullable - as bool, - )); - } } /// @nodoc @JsonSerializable() -class _$PlayerSettingsImpl implements _PlayerSettings { - const _$PlayerSettingsImpl( - {this.miniPlayerSettings = const MinimizedPlayerSettings(), - this.expandedPlayerSettings = const ExpandedPlayerSettings(), - this.preferredDefaultVolume = 1, - this.preferredDefaultSpeed = 1, - final List speedOptions = const [1, 1.25, 1.5, 1.75, 2], - this.speedIncrement = 0.05, - this.minSpeed = 0.1, - this.maxSpeed = 4, - this.minimumPositionForReporting = const Duration(seconds: 10), - this.playbackReportInterval = const Duration(seconds: 10), - this.markCompleteWhenTimeLeft = const Duration(seconds: 15), - this.configurePlayerForEveryBook = true}) - : _speedOptions = speedOptions; - factory _$PlayerSettingsImpl.fromJson(Map json) => - _$$PlayerSettingsImplFromJson(json); +class _PlayerSettings implements PlayerSettings { + const _PlayerSettings({this.miniPlayerSettings = const MinimizedPlayerSettings(), this.expandedPlayerSettings = const ExpandedPlayerSettings(), this.preferredDefaultVolume = 1, this.preferredDefaultSpeed = 1, final List speedOptions = const [1, 1.25, 1.5, 1.75, 2], this.speedIncrement = 0.05, this.minSpeed = 0.1, this.maxSpeed = 4, this.minimumPositionForReporting = const Duration(seconds: 10), this.playbackReportInterval = const Duration(seconds: 10), this.markCompleteWhenTimeLeft = const Duration(seconds: 15), this.configurePlayerForEveryBook = true}): _speedOptions = speedOptions; + factory _PlayerSettings.fromJson(Map json) => _$PlayerSettingsFromJson(json); - @override - @JsonKey() - final MinimizedPlayerSettings miniPlayerSettings; - @override - @JsonKey() - final ExpandedPlayerSettings expandedPlayerSettings; - @override - @JsonKey() - final double preferredDefaultVolume; - @override - @JsonKey() - final double preferredDefaultSpeed; - final List _speedOptions; - @override - @JsonKey() - List get speedOptions { - if (_speedOptions is EqualUnmodifiableListView) return _speedOptions; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_speedOptions); - } - - @override - @JsonKey() - final double speedIncrement; - @override - @JsonKey() - final double minSpeed; - @override - @JsonKey() - final double maxSpeed; - @override - @JsonKey() - final Duration minimumPositionForReporting; - @override - @JsonKey() - final Duration playbackReportInterval; - @override - @JsonKey() - final Duration markCompleteWhenTimeLeft; - @override - @JsonKey() - final bool configurePlayerForEveryBook; - - @override - String toString() { - return 'PlayerSettings(miniPlayerSettings: $miniPlayerSettings, expandedPlayerSettings: $expandedPlayerSettings, preferredDefaultVolume: $preferredDefaultVolume, preferredDefaultSpeed: $preferredDefaultSpeed, speedOptions: $speedOptions, speedIncrement: $speedIncrement, minSpeed: $minSpeed, maxSpeed: $maxSpeed, minimumPositionForReporting: $minimumPositionForReporting, playbackReportInterval: $playbackReportInterval, markCompleteWhenTimeLeft: $markCompleteWhenTimeLeft, configurePlayerForEveryBook: $configurePlayerForEveryBook)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$PlayerSettingsImpl && - (identical(other.miniPlayerSettings, miniPlayerSettings) || - other.miniPlayerSettings == miniPlayerSettings) && - (identical(other.expandedPlayerSettings, expandedPlayerSettings) || - other.expandedPlayerSettings == expandedPlayerSettings) && - (identical(other.preferredDefaultVolume, preferredDefaultVolume) || - other.preferredDefaultVolume == preferredDefaultVolume) && - (identical(other.preferredDefaultSpeed, preferredDefaultSpeed) || - other.preferredDefaultSpeed == preferredDefaultSpeed) && - const DeepCollectionEquality() - .equals(other._speedOptions, _speedOptions) && - (identical(other.speedIncrement, speedIncrement) || - other.speedIncrement == speedIncrement) && - (identical(other.minSpeed, minSpeed) || - other.minSpeed == minSpeed) && - (identical(other.maxSpeed, maxSpeed) || - other.maxSpeed == maxSpeed) && - (identical(other.minimumPositionForReporting, - minimumPositionForReporting) || - other.minimumPositionForReporting == - minimumPositionForReporting) && - (identical(other.playbackReportInterval, playbackReportInterval) || - other.playbackReportInterval == playbackReportInterval) && - (identical( - other.markCompleteWhenTimeLeft, markCompleteWhenTimeLeft) || - other.markCompleteWhenTimeLeft == markCompleteWhenTimeLeft) && - (identical(other.configurePlayerForEveryBook, - configurePlayerForEveryBook) || - other.configurePlayerForEveryBook == - configurePlayerForEveryBook)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - miniPlayerSettings, - expandedPlayerSettings, - preferredDefaultVolume, - preferredDefaultSpeed, - const DeepCollectionEquality().hash(_speedOptions), - speedIncrement, - minSpeed, - maxSpeed, - minimumPositionForReporting, - playbackReportInterval, - markCompleteWhenTimeLeft, - configurePlayerForEveryBook); - - /// Create a copy of PlayerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$PlayerSettingsImplCopyWith<_$PlayerSettingsImpl> get copyWith => - __$$PlayerSettingsImplCopyWithImpl<_$PlayerSettingsImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$PlayerSettingsImplToJson( - this, - ); - } +@override@JsonKey() final MinimizedPlayerSettings miniPlayerSettings; +@override@JsonKey() final ExpandedPlayerSettings expandedPlayerSettings; +@override@JsonKey() final double preferredDefaultVolume; +@override@JsonKey() final double preferredDefaultSpeed; + final List _speedOptions; +@override@JsonKey() List get speedOptions { + if (_speedOptions is EqualUnmodifiableListView) return _speedOptions; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_speedOptions); } -abstract class _PlayerSettings implements PlayerSettings { - const factory _PlayerSettings( - {final MinimizedPlayerSettings miniPlayerSettings, - final ExpandedPlayerSettings expandedPlayerSettings, - final double preferredDefaultVolume, - final double preferredDefaultSpeed, - final List speedOptions, - final double speedIncrement, - final double minSpeed, - final double maxSpeed, - final Duration minimumPositionForReporting, - final Duration playbackReportInterval, - final Duration markCompleteWhenTimeLeft, - final bool configurePlayerForEveryBook}) = _$PlayerSettingsImpl; +@override@JsonKey() final double speedIncrement; +@override@JsonKey() final double minSpeed; +@override@JsonKey() final double maxSpeed; +@override@JsonKey() final Duration minimumPositionForReporting; +@override@JsonKey() final Duration playbackReportInterval; +@override@JsonKey() final Duration markCompleteWhenTimeLeft; +@override@JsonKey() final bool configurePlayerForEveryBook; - factory _PlayerSettings.fromJson(Map json) = - _$PlayerSettingsImpl.fromJson; +/// Create a copy of PlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$PlayerSettingsCopyWith<_PlayerSettings> get copyWith => __$PlayerSettingsCopyWithImpl<_PlayerSettings>(this, _$identity); - @override - MinimizedPlayerSettings get miniPlayerSettings; - @override - ExpandedPlayerSettings get expandedPlayerSettings; - @override - double get preferredDefaultVolume; - @override - double get preferredDefaultSpeed; - @override - List get speedOptions; - @override - double get speedIncrement; - @override - double get minSpeed; - @override - double get maxSpeed; - @override - Duration get minimumPositionForReporting; - @override - Duration get playbackReportInterval; - @override - Duration get markCompleteWhenTimeLeft; - @override - bool get configurePlayerForEveryBook; - - /// Create a copy of PlayerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$PlayerSettingsImplCopyWith<_$PlayerSettingsImpl> get copyWith => - throw _privateConstructorUsedError; +@override +Map toJson() { + return _$PlayerSettingsToJson(this, ); } -ExpandedPlayerSettings _$ExpandedPlayerSettingsFromJson( - Map json) { - return _ExpandedPlayerSettings.fromJson(json); +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _PlayerSettings&&(identical(other.miniPlayerSettings, miniPlayerSettings) || other.miniPlayerSettings == miniPlayerSettings)&&(identical(other.expandedPlayerSettings, expandedPlayerSettings) || other.expandedPlayerSettings == expandedPlayerSettings)&&(identical(other.preferredDefaultVolume, preferredDefaultVolume) || other.preferredDefaultVolume == preferredDefaultVolume)&&(identical(other.preferredDefaultSpeed, preferredDefaultSpeed) || other.preferredDefaultSpeed == preferredDefaultSpeed)&&const DeepCollectionEquality().equals(other._speedOptions, _speedOptions)&&(identical(other.speedIncrement, speedIncrement) || other.speedIncrement == speedIncrement)&&(identical(other.minSpeed, minSpeed) || other.minSpeed == minSpeed)&&(identical(other.maxSpeed, maxSpeed) || other.maxSpeed == maxSpeed)&&(identical(other.minimumPositionForReporting, minimumPositionForReporting) || other.minimumPositionForReporting == minimumPositionForReporting)&&(identical(other.playbackReportInterval, playbackReportInterval) || other.playbackReportInterval == playbackReportInterval)&&(identical(other.markCompleteWhenTimeLeft, markCompleteWhenTimeLeft) || other.markCompleteWhenTimeLeft == markCompleteWhenTimeLeft)&&(identical(other.configurePlayerForEveryBook, configurePlayerForEveryBook) || other.configurePlayerForEveryBook == configurePlayerForEveryBook)); } +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,miniPlayerSettings,expandedPlayerSettings,preferredDefaultVolume,preferredDefaultSpeed,const DeepCollectionEquality().hash(_speedOptions),speedIncrement,minSpeed,maxSpeed,minimumPositionForReporting,playbackReportInterval,markCompleteWhenTimeLeft,configurePlayerForEveryBook); + +@override +String toString() { + return 'PlayerSettings(miniPlayerSettings: $miniPlayerSettings, expandedPlayerSettings: $expandedPlayerSettings, preferredDefaultVolume: $preferredDefaultVolume, preferredDefaultSpeed: $preferredDefaultSpeed, speedOptions: $speedOptions, speedIncrement: $speedIncrement, minSpeed: $minSpeed, maxSpeed: $maxSpeed, minimumPositionForReporting: $minimumPositionForReporting, playbackReportInterval: $playbackReportInterval, markCompleteWhenTimeLeft: $markCompleteWhenTimeLeft, configurePlayerForEveryBook: $configurePlayerForEveryBook)'; +} + + +} + +/// @nodoc +abstract mixin class _$PlayerSettingsCopyWith<$Res> implements $PlayerSettingsCopyWith<$Res> { + factory _$PlayerSettingsCopyWith(_PlayerSettings value, $Res Function(_PlayerSettings) _then) = __$PlayerSettingsCopyWithImpl; +@override @useResult +$Res call({ + MinimizedPlayerSettings miniPlayerSettings, ExpandedPlayerSettings expandedPlayerSettings, double preferredDefaultVolume, double preferredDefaultSpeed, List speedOptions, double speedIncrement, double minSpeed, double maxSpeed, Duration minimumPositionForReporting, Duration playbackReportInterval, Duration markCompleteWhenTimeLeft, bool configurePlayerForEveryBook +}); + + +@override $MinimizedPlayerSettingsCopyWith<$Res> get miniPlayerSettings;@override $ExpandedPlayerSettingsCopyWith<$Res> get expandedPlayerSettings; + +} +/// @nodoc +class __$PlayerSettingsCopyWithImpl<$Res> + implements _$PlayerSettingsCopyWith<$Res> { + __$PlayerSettingsCopyWithImpl(this._self, this._then); + + final _PlayerSettings _self; + final $Res Function(_PlayerSettings) _then; + +/// Create a copy of PlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? miniPlayerSettings = null,Object? expandedPlayerSettings = null,Object? preferredDefaultVolume = null,Object? preferredDefaultSpeed = null,Object? speedOptions = null,Object? speedIncrement = null,Object? minSpeed = null,Object? maxSpeed = null,Object? minimumPositionForReporting = null,Object? playbackReportInterval = null,Object? markCompleteWhenTimeLeft = null,Object? configurePlayerForEveryBook = null,}) { + return _then(_PlayerSettings( +miniPlayerSettings: null == miniPlayerSettings ? _self.miniPlayerSettings : miniPlayerSettings // ignore: cast_nullable_to_non_nullable +as MinimizedPlayerSettings,expandedPlayerSettings: null == expandedPlayerSettings ? _self.expandedPlayerSettings : expandedPlayerSettings // ignore: cast_nullable_to_non_nullable +as ExpandedPlayerSettings,preferredDefaultVolume: null == preferredDefaultVolume ? _self.preferredDefaultVolume : preferredDefaultVolume // ignore: cast_nullable_to_non_nullable +as double,preferredDefaultSpeed: null == preferredDefaultSpeed ? _self.preferredDefaultSpeed : preferredDefaultSpeed // ignore: cast_nullable_to_non_nullable +as double,speedOptions: null == speedOptions ? _self._speedOptions : speedOptions // ignore: cast_nullable_to_non_nullable +as List,speedIncrement: null == speedIncrement ? _self.speedIncrement : speedIncrement // ignore: cast_nullable_to_non_nullable +as double,minSpeed: null == minSpeed ? _self.minSpeed : minSpeed // ignore: cast_nullable_to_non_nullable +as double,maxSpeed: null == maxSpeed ? _self.maxSpeed : maxSpeed // ignore: cast_nullable_to_non_nullable +as double,minimumPositionForReporting: null == minimumPositionForReporting ? _self.minimumPositionForReporting : minimumPositionForReporting // ignore: cast_nullable_to_non_nullable +as Duration,playbackReportInterval: null == playbackReportInterval ? _self.playbackReportInterval : playbackReportInterval // ignore: cast_nullable_to_non_nullable +as Duration,markCompleteWhenTimeLeft: null == markCompleteWhenTimeLeft ? _self.markCompleteWhenTimeLeft : markCompleteWhenTimeLeft // ignore: cast_nullable_to_non_nullable +as Duration,configurePlayerForEveryBook: null == configurePlayerForEveryBook ? _self.configurePlayerForEveryBook : configurePlayerForEveryBook // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + +/// Create a copy of PlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$MinimizedPlayerSettingsCopyWith<$Res> get miniPlayerSettings { + + return $MinimizedPlayerSettingsCopyWith<$Res>(_self.miniPlayerSettings, (value) { + return _then(_self.copyWith(miniPlayerSettings: value)); + }); +}/// Create a copy of PlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ExpandedPlayerSettingsCopyWith<$Res> get expandedPlayerSettings { + + return $ExpandedPlayerSettingsCopyWith<$Res>(_self.expandedPlayerSettings, (value) { + return _then(_self.copyWith(expandedPlayerSettings: value)); + }); +} +} + + /// @nodoc mixin _$ExpandedPlayerSettings { - bool get showTotalProgress => throw _privateConstructorUsedError; - bool get showChapterProgress => throw _privateConstructorUsedError; + + bool get showTotalProgress; bool get showChapterProgress; +/// Create a copy of ExpandedPlayerSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ExpandedPlayerSettingsCopyWith get copyWith => _$ExpandedPlayerSettingsCopyWithImpl(this as ExpandedPlayerSettings, _$identity); /// Serializes this ExpandedPlayerSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ExpandedPlayerSettings&&(identical(other.showTotalProgress, showTotalProgress) || other.showTotalProgress == showTotalProgress)&&(identical(other.showChapterProgress, showChapterProgress) || other.showChapterProgress == showChapterProgress)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,showTotalProgress,showChapterProgress); + +@override +String toString() { + return 'ExpandedPlayerSettings(showTotalProgress: $showTotalProgress, showChapterProgress: $showChapterProgress)'; +} + - /// Create a copy of ExpandedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ExpandedPlayerSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $ExpandedPlayerSettingsCopyWith<$Res> { - factory $ExpandedPlayerSettingsCopyWith(ExpandedPlayerSettings value, - $Res Function(ExpandedPlayerSettings) then) = - _$ExpandedPlayerSettingsCopyWithImpl<$Res, ExpandedPlayerSettings>; - @useResult - $Res call({bool showTotalProgress, bool showChapterProgress}); -} +abstract mixin class $ExpandedPlayerSettingsCopyWith<$Res> { + factory $ExpandedPlayerSettingsCopyWith(ExpandedPlayerSettings value, $Res Function(ExpandedPlayerSettings) _then) = _$ExpandedPlayerSettingsCopyWithImpl; +@useResult +$Res call({ + bool showTotalProgress, bool showChapterProgress +}); + + + +} /// @nodoc -class _$ExpandedPlayerSettingsCopyWithImpl<$Res, - $Val extends ExpandedPlayerSettings> +class _$ExpandedPlayerSettingsCopyWithImpl<$Res> implements $ExpandedPlayerSettingsCopyWith<$Res> { - _$ExpandedPlayerSettingsCopyWithImpl(this._value, this._then); + _$ExpandedPlayerSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final ExpandedPlayerSettings _self; + final $Res Function(ExpandedPlayerSettings) _then; - /// Create a copy of ExpandedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? showTotalProgress = null, - Object? showChapterProgress = null, - }) { - return _then(_value.copyWith( - showTotalProgress: null == showTotalProgress - ? _value.showTotalProgress - : showTotalProgress // ignore: cast_nullable_to_non_nullable - as bool, - showChapterProgress: null == showChapterProgress - ? _value.showChapterProgress - : showChapterProgress // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } +/// Create a copy of ExpandedPlayerSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? showTotalProgress = null,Object? showChapterProgress = null,}) { + return _then(_self.copyWith( +showTotalProgress: null == showTotalProgress ? _self.showTotalProgress : showTotalProgress // ignore: cast_nullable_to_non_nullable +as bool,showChapterProgress: null == showChapterProgress ? _self.showChapterProgress : showChapterProgress // ignore: cast_nullable_to_non_nullable +as bool, + )); } -/// @nodoc -abstract class _$$ExpandedPlayerSettingsImplCopyWith<$Res> - implements $ExpandedPlayerSettingsCopyWith<$Res> { - factory _$$ExpandedPlayerSettingsImplCopyWith( - _$ExpandedPlayerSettingsImpl value, - $Res Function(_$ExpandedPlayerSettingsImpl) then) = - __$$ExpandedPlayerSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({bool showTotalProgress, bool showChapterProgress}); } -/// @nodoc -class __$$ExpandedPlayerSettingsImplCopyWithImpl<$Res> - extends _$ExpandedPlayerSettingsCopyWithImpl<$Res, - _$ExpandedPlayerSettingsImpl> - implements _$$ExpandedPlayerSettingsImplCopyWith<$Res> { - __$$ExpandedPlayerSettingsImplCopyWithImpl( - _$ExpandedPlayerSettingsImpl _value, - $Res Function(_$ExpandedPlayerSettingsImpl) _then) - : super(_value, _then); - /// Create a copy of ExpandedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? showTotalProgress = null, - Object? showChapterProgress = null, - }) { - return _then(_$ExpandedPlayerSettingsImpl( - showTotalProgress: null == showTotalProgress - ? _value.showTotalProgress - : showTotalProgress // ignore: cast_nullable_to_non_nullable - as bool, - showChapterProgress: null == showChapterProgress - ? _value.showChapterProgress - : showChapterProgress // ignore: cast_nullable_to_non_nullable - as bool, - )); - } +/// Adds pattern-matching-related methods to [ExpandedPlayerSettings]. +extension ExpandedPlayerSettingsPatterns on ExpandedPlayerSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ExpandedPlayerSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ExpandedPlayerSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ExpandedPlayerSettings value) $default,){ +final _that = this; +switch (_that) { +case _ExpandedPlayerSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ExpandedPlayerSettings value)? $default,){ +final _that = this; +switch (_that) { +case _ExpandedPlayerSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool showTotalProgress, bool showChapterProgress)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ExpandedPlayerSettings() when $default != null: +return $default(_that.showTotalProgress,_that.showChapterProgress);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool showTotalProgress, bool showChapterProgress) $default,) {final _that = this; +switch (_that) { +case _ExpandedPlayerSettings(): +return $default(_that.showTotalProgress,_that.showChapterProgress);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool showTotalProgress, bool showChapterProgress)? $default,) {final _that = this; +switch (_that) { +case _ExpandedPlayerSettings() when $default != null: +return $default(_that.showTotalProgress,_that.showChapterProgress);case _: + return null; + +} +} + } /// @nodoc @JsonSerializable() -class _$ExpandedPlayerSettingsImpl implements _ExpandedPlayerSettings { - const _$ExpandedPlayerSettingsImpl( - {this.showTotalProgress = false, this.showChapterProgress = true}); - factory _$ExpandedPlayerSettingsImpl.fromJson(Map json) => - _$$ExpandedPlayerSettingsImplFromJson(json); +class _ExpandedPlayerSettings implements ExpandedPlayerSettings { + const _ExpandedPlayerSettings({this.showTotalProgress = false, this.showChapterProgress = true}); + factory _ExpandedPlayerSettings.fromJson(Map json) => _$ExpandedPlayerSettingsFromJson(json); - @override - @JsonKey() - final bool showTotalProgress; - @override - @JsonKey() - final bool showChapterProgress; +@override@JsonKey() final bool showTotalProgress; +@override@JsonKey() final bool showChapterProgress; - @override - String toString() { - return 'ExpandedPlayerSettings(showTotalProgress: $showTotalProgress, showChapterProgress: $showChapterProgress)'; - } +/// Create a copy of ExpandedPlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ExpandedPlayerSettingsCopyWith<_ExpandedPlayerSettings> get copyWith => __$ExpandedPlayerSettingsCopyWithImpl<_ExpandedPlayerSettings>(this, _$identity); - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ExpandedPlayerSettingsImpl && - (identical(other.showTotalProgress, showTotalProgress) || - other.showTotalProgress == showTotalProgress) && - (identical(other.showChapterProgress, showChapterProgress) || - other.showChapterProgress == showChapterProgress)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => - Object.hash(runtimeType, showTotalProgress, showChapterProgress); - - /// Create a copy of ExpandedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ExpandedPlayerSettingsImplCopyWith<_$ExpandedPlayerSettingsImpl> - get copyWith => __$$ExpandedPlayerSettingsImplCopyWithImpl< - _$ExpandedPlayerSettingsImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ExpandedPlayerSettingsImplToJson( - this, - ); - } +@override +Map toJson() { + return _$ExpandedPlayerSettingsToJson(this, ); } -abstract class _ExpandedPlayerSettings implements ExpandedPlayerSettings { - const factory _ExpandedPlayerSettings( - {final bool showTotalProgress, - final bool showChapterProgress}) = _$ExpandedPlayerSettingsImpl; - - factory _ExpandedPlayerSettings.fromJson(Map json) = - _$ExpandedPlayerSettingsImpl.fromJson; - - @override - bool get showTotalProgress; - @override - bool get showChapterProgress; - - /// Create a copy of ExpandedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ExpandedPlayerSettingsImplCopyWith<_$ExpandedPlayerSettingsImpl> - get copyWith => throw _privateConstructorUsedError; +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ExpandedPlayerSettings&&(identical(other.showTotalProgress, showTotalProgress) || other.showTotalProgress == showTotalProgress)&&(identical(other.showChapterProgress, showChapterProgress) || other.showChapterProgress == showChapterProgress)); } -MinimizedPlayerSettings _$MinimizedPlayerSettingsFromJson( - Map json) { - return _MinimizedPlayerSettings.fromJson(json); +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,showTotalProgress,showChapterProgress); + +@override +String toString() { + return 'ExpandedPlayerSettings(showTotalProgress: $showTotalProgress, showChapterProgress: $showChapterProgress)'; } + +} + +/// @nodoc +abstract mixin class _$ExpandedPlayerSettingsCopyWith<$Res> implements $ExpandedPlayerSettingsCopyWith<$Res> { + factory _$ExpandedPlayerSettingsCopyWith(_ExpandedPlayerSettings value, $Res Function(_ExpandedPlayerSettings) _then) = __$ExpandedPlayerSettingsCopyWithImpl; +@override @useResult +$Res call({ + bool showTotalProgress, bool showChapterProgress +}); + + + + +} +/// @nodoc +class __$ExpandedPlayerSettingsCopyWithImpl<$Res> + implements _$ExpandedPlayerSettingsCopyWith<$Res> { + __$ExpandedPlayerSettingsCopyWithImpl(this._self, this._then); + + final _ExpandedPlayerSettings _self; + final $Res Function(_ExpandedPlayerSettings) _then; + +/// Create a copy of ExpandedPlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? showTotalProgress = null,Object? showChapterProgress = null,}) { + return _then(_ExpandedPlayerSettings( +showTotalProgress: null == showTotalProgress ? _self.showTotalProgress : showTotalProgress // ignore: cast_nullable_to_non_nullable +as bool,showChapterProgress: null == showChapterProgress ? _self.showChapterProgress : showChapterProgress // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + + +} + + /// @nodoc mixin _$MinimizedPlayerSettings { - bool get useChapterInfo => throw _privateConstructorUsedError; + + bool get useChapterInfo; +/// Create a copy of MinimizedPlayerSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$MinimizedPlayerSettingsCopyWith get copyWith => _$MinimizedPlayerSettingsCopyWithImpl(this as MinimizedPlayerSettings, _$identity); /// Serializes this MinimizedPlayerSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is MinimizedPlayerSettings&&(identical(other.useChapterInfo, useChapterInfo) || other.useChapterInfo == useChapterInfo)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,useChapterInfo); + +@override +String toString() { + return 'MinimizedPlayerSettings(useChapterInfo: $useChapterInfo)'; +} + - /// Create a copy of MinimizedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $MinimizedPlayerSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $MinimizedPlayerSettingsCopyWith<$Res> { - factory $MinimizedPlayerSettingsCopyWith(MinimizedPlayerSettings value, - $Res Function(MinimizedPlayerSettings) then) = - _$MinimizedPlayerSettingsCopyWithImpl<$Res, MinimizedPlayerSettings>; - @useResult - $Res call({bool useChapterInfo}); -} +abstract mixin class $MinimizedPlayerSettingsCopyWith<$Res> { + factory $MinimizedPlayerSettingsCopyWith(MinimizedPlayerSettings value, $Res Function(MinimizedPlayerSettings) _then) = _$MinimizedPlayerSettingsCopyWithImpl; +@useResult +$Res call({ + bool useChapterInfo +}); + + + +} /// @nodoc -class _$MinimizedPlayerSettingsCopyWithImpl<$Res, - $Val extends MinimizedPlayerSettings> +class _$MinimizedPlayerSettingsCopyWithImpl<$Res> implements $MinimizedPlayerSettingsCopyWith<$Res> { - _$MinimizedPlayerSettingsCopyWithImpl(this._value, this._then); + _$MinimizedPlayerSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final MinimizedPlayerSettings _self; + final $Res Function(MinimizedPlayerSettings) _then; - /// Create a copy of MinimizedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? useChapterInfo = null, - }) { - return _then(_value.copyWith( - useChapterInfo: null == useChapterInfo - ? _value.useChapterInfo - : useChapterInfo // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } +/// Create a copy of MinimizedPlayerSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? useChapterInfo = null,}) { + return _then(_self.copyWith( +useChapterInfo: null == useChapterInfo ? _self.useChapterInfo : useChapterInfo // ignore: cast_nullable_to_non_nullable +as bool, + )); } -/// @nodoc -abstract class _$$MinimizedPlayerSettingsImplCopyWith<$Res> - implements $MinimizedPlayerSettingsCopyWith<$Res> { - factory _$$MinimizedPlayerSettingsImplCopyWith( - _$MinimizedPlayerSettingsImpl value, - $Res Function(_$MinimizedPlayerSettingsImpl) then) = - __$$MinimizedPlayerSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({bool useChapterInfo}); } -/// @nodoc -class __$$MinimizedPlayerSettingsImplCopyWithImpl<$Res> - extends _$MinimizedPlayerSettingsCopyWithImpl<$Res, - _$MinimizedPlayerSettingsImpl> - implements _$$MinimizedPlayerSettingsImplCopyWith<$Res> { - __$$MinimizedPlayerSettingsImplCopyWithImpl( - _$MinimizedPlayerSettingsImpl _value, - $Res Function(_$MinimizedPlayerSettingsImpl) _then) - : super(_value, _then); - /// Create a copy of MinimizedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? useChapterInfo = null, - }) { - return _then(_$MinimizedPlayerSettingsImpl( - useChapterInfo: null == useChapterInfo - ? _value.useChapterInfo - : useChapterInfo // ignore: cast_nullable_to_non_nullable - as bool, - )); - } +/// Adds pattern-matching-related methods to [MinimizedPlayerSettings]. +extension MinimizedPlayerSettingsPatterns on MinimizedPlayerSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _MinimizedPlayerSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _MinimizedPlayerSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _MinimizedPlayerSettings value) $default,){ +final _that = this; +switch (_that) { +case _MinimizedPlayerSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _MinimizedPlayerSettings value)? $default,){ +final _that = this; +switch (_that) { +case _MinimizedPlayerSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool useChapterInfo)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _MinimizedPlayerSettings() when $default != null: +return $default(_that.useChapterInfo);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool useChapterInfo) $default,) {final _that = this; +switch (_that) { +case _MinimizedPlayerSettings(): +return $default(_that.useChapterInfo);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool useChapterInfo)? $default,) {final _that = this; +switch (_that) { +case _MinimizedPlayerSettings() when $default != null: +return $default(_that.useChapterInfo);case _: + return null; + +} +} + } /// @nodoc @JsonSerializable() -class _$MinimizedPlayerSettingsImpl implements _MinimizedPlayerSettings { - const _$MinimizedPlayerSettingsImpl({this.useChapterInfo = false}); - factory _$MinimizedPlayerSettingsImpl.fromJson(Map json) => - _$$MinimizedPlayerSettingsImplFromJson(json); +class _MinimizedPlayerSettings implements MinimizedPlayerSettings { + const _MinimizedPlayerSettings({this.useChapterInfo = false}); + factory _MinimizedPlayerSettings.fromJson(Map json) => _$MinimizedPlayerSettingsFromJson(json); - @override - @JsonKey() - final bool useChapterInfo; +@override@JsonKey() final bool useChapterInfo; - @override - String toString() { - return 'MinimizedPlayerSettings(useChapterInfo: $useChapterInfo)'; - } +/// Create a copy of MinimizedPlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$MinimizedPlayerSettingsCopyWith<_MinimizedPlayerSettings> get copyWith => __$MinimizedPlayerSettingsCopyWithImpl<_MinimizedPlayerSettings>(this, _$identity); - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$MinimizedPlayerSettingsImpl && - (identical(other.useChapterInfo, useChapterInfo) || - other.useChapterInfo == useChapterInfo)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, useChapterInfo); - - /// Create a copy of MinimizedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$MinimizedPlayerSettingsImplCopyWith<_$MinimizedPlayerSettingsImpl> - get copyWith => __$$MinimizedPlayerSettingsImplCopyWithImpl< - _$MinimizedPlayerSettingsImpl>(this, _$identity); - - @override - Map toJson() { - return _$$MinimizedPlayerSettingsImplToJson( - this, - ); - } +@override +Map toJson() { + return _$MinimizedPlayerSettingsToJson(this, ); } -abstract class _MinimizedPlayerSettings implements MinimizedPlayerSettings { - const factory _MinimizedPlayerSettings({final bool useChapterInfo}) = - _$MinimizedPlayerSettingsImpl; - - factory _MinimizedPlayerSettings.fromJson(Map json) = - _$MinimizedPlayerSettingsImpl.fromJson; - - @override - bool get useChapterInfo; - - /// Create a copy of MinimizedPlayerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$MinimizedPlayerSettingsImplCopyWith<_$MinimizedPlayerSettingsImpl> - get copyWith => throw _privateConstructorUsedError; +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _MinimizedPlayerSettings&&(identical(other.useChapterInfo, useChapterInfo) || other.useChapterInfo == useChapterInfo)); } -SleepTimerSettings _$SleepTimerSettingsFromJson(Map json) { - return _SleepTimerSettings.fromJson(json); +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,useChapterInfo); + +@override +String toString() { + return 'MinimizedPlayerSettings(useChapterInfo: $useChapterInfo)'; } + +} + +/// @nodoc +abstract mixin class _$MinimizedPlayerSettingsCopyWith<$Res> implements $MinimizedPlayerSettingsCopyWith<$Res> { + factory _$MinimizedPlayerSettingsCopyWith(_MinimizedPlayerSettings value, $Res Function(_MinimizedPlayerSettings) _then) = __$MinimizedPlayerSettingsCopyWithImpl; +@override @useResult +$Res call({ + bool useChapterInfo +}); + + + + +} +/// @nodoc +class __$MinimizedPlayerSettingsCopyWithImpl<$Res> + implements _$MinimizedPlayerSettingsCopyWith<$Res> { + __$MinimizedPlayerSettingsCopyWithImpl(this._self, this._then); + + final _MinimizedPlayerSettings _self; + final $Res Function(_MinimizedPlayerSettings) _then; + +/// Create a copy of MinimizedPlayerSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? useChapterInfo = null,}) { + return _then(_MinimizedPlayerSettings( +useChapterInfo: null == useChapterInfo ? _self.useChapterInfo : useChapterInfo // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + + +} + + /// @nodoc mixin _$SleepTimerSettings { - Duration get defaultDuration => throw _privateConstructorUsedError; - List get presetDurations => throw _privateConstructorUsedError; - Duration get maxDuration => throw _privateConstructorUsedError; - bool get fadeOutAudio => throw _privateConstructorUsedError; - Duration get fadeOutDuration => throw _privateConstructorUsedError; - /// if true, the player will automatically rewind the audio when the sleep timer is stopped - bool get autoRewindWhenStopped => throw _privateConstructorUsedError; - - /// the key is the duration in minutes - Map get autoRewindDurations => - throw _privateConstructorUsedError; - - /// auto turn on timer settings - bool get autoTurnOnTimer => throw _privateConstructorUsedError; - - /// always auto turn on timer settings or during specific times - bool get alwaysAutoTurnOnTimer => throw _privateConstructorUsedError; - - /// auto timer settings, only used if [alwaysAutoTurnOnTimer] is false - /// - /// duration is the time from 00:00 - Duration get autoTurnOnTime => throw _privateConstructorUsedError; - Duration get autoTurnOffTime => throw _privateConstructorUsedError; + Duration get defaultDuration; List get presetDurations; Duration get maxDuration; bool get fadeOutAudio; Duration get fadeOutDuration;/// if true, the player will automatically rewind the audio when the sleep timer is stopped + bool get autoRewindWhenStopped;/// the key is the duration in minutes + Map get autoRewindDurations;/// auto turn on timer settings + bool get autoTurnOnTimer;/// always auto turn on timer settings or during specific times + bool get alwaysAutoTurnOnTimer;/// auto timer settings, only used if [alwaysAutoTurnOnTimer] is false +/// +/// duration is the time from 00:00 + Duration get autoTurnOnTime; Duration get autoTurnOffTime; +/// Create a copy of SleepTimerSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SleepTimerSettingsCopyWith get copyWith => _$SleepTimerSettingsCopyWithImpl(this as SleepTimerSettings, _$identity); /// Serializes this SleepTimerSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SleepTimerSettings&&(identical(other.defaultDuration, defaultDuration) || other.defaultDuration == defaultDuration)&&const DeepCollectionEquality().equals(other.presetDurations, presetDurations)&&(identical(other.maxDuration, maxDuration) || other.maxDuration == maxDuration)&&(identical(other.fadeOutAudio, fadeOutAudio) || other.fadeOutAudio == fadeOutAudio)&&(identical(other.fadeOutDuration, fadeOutDuration) || other.fadeOutDuration == fadeOutDuration)&&(identical(other.autoRewindWhenStopped, autoRewindWhenStopped) || other.autoRewindWhenStopped == autoRewindWhenStopped)&&const DeepCollectionEquality().equals(other.autoRewindDurations, autoRewindDurations)&&(identical(other.autoTurnOnTimer, autoTurnOnTimer) || other.autoTurnOnTimer == autoTurnOnTimer)&&(identical(other.alwaysAutoTurnOnTimer, alwaysAutoTurnOnTimer) || other.alwaysAutoTurnOnTimer == alwaysAutoTurnOnTimer)&&(identical(other.autoTurnOnTime, autoTurnOnTime) || other.autoTurnOnTime == autoTurnOnTime)&&(identical(other.autoTurnOffTime, autoTurnOffTime) || other.autoTurnOffTime == autoTurnOffTime)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,defaultDuration,const DeepCollectionEquality().hash(presetDurations),maxDuration,fadeOutAudio,fadeOutDuration,autoRewindWhenStopped,const DeepCollectionEquality().hash(autoRewindDurations),autoTurnOnTimer,alwaysAutoTurnOnTimer,autoTurnOnTime,autoTurnOffTime); + +@override +String toString() { + return 'SleepTimerSettings(defaultDuration: $defaultDuration, presetDurations: $presetDurations, maxDuration: $maxDuration, fadeOutAudio: $fadeOutAudio, fadeOutDuration: $fadeOutDuration, autoRewindWhenStopped: $autoRewindWhenStopped, autoRewindDurations: $autoRewindDurations, autoTurnOnTimer: $autoTurnOnTimer, alwaysAutoTurnOnTimer: $alwaysAutoTurnOnTimer, autoTurnOnTime: $autoTurnOnTime, autoTurnOffTime: $autoTurnOffTime)'; +} + - /// Create a copy of SleepTimerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $SleepTimerSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $SleepTimerSettingsCopyWith<$Res> { - factory $SleepTimerSettingsCopyWith( - SleepTimerSettings value, $Res Function(SleepTimerSettings) then) = - _$SleepTimerSettingsCopyWithImpl<$Res, SleepTimerSettings>; - @useResult - $Res call( - {Duration defaultDuration, - List presetDurations, - Duration maxDuration, - bool fadeOutAudio, - Duration fadeOutDuration, - bool autoRewindWhenStopped, - Map autoRewindDurations, - bool autoTurnOnTimer, - bool alwaysAutoTurnOnTimer, - Duration autoTurnOnTime, - Duration autoTurnOffTime}); -} +abstract mixin class $SleepTimerSettingsCopyWith<$Res> { + factory $SleepTimerSettingsCopyWith(SleepTimerSettings value, $Res Function(SleepTimerSettings) _then) = _$SleepTimerSettingsCopyWithImpl; +@useResult +$Res call({ + Duration defaultDuration, List presetDurations, Duration maxDuration, bool fadeOutAudio, Duration fadeOutDuration, bool autoRewindWhenStopped, Map autoRewindDurations, bool autoTurnOnTimer, bool alwaysAutoTurnOnTimer, Duration autoTurnOnTime, Duration autoTurnOffTime +}); + + + +} /// @nodoc -class _$SleepTimerSettingsCopyWithImpl<$Res, $Val extends SleepTimerSettings> +class _$SleepTimerSettingsCopyWithImpl<$Res> implements $SleepTimerSettingsCopyWith<$Res> { - _$SleepTimerSettingsCopyWithImpl(this._value, this._then); + _$SleepTimerSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final SleepTimerSettings _self; + final $Res Function(SleepTimerSettings) _then; - /// Create a copy of SleepTimerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? defaultDuration = null, - Object? presetDurations = null, - Object? maxDuration = null, - Object? fadeOutAudio = null, - Object? fadeOutDuration = null, - Object? autoRewindWhenStopped = null, - Object? autoRewindDurations = null, - Object? autoTurnOnTimer = null, - Object? alwaysAutoTurnOnTimer = null, - Object? autoTurnOnTime = null, - Object? autoTurnOffTime = null, - }) { - return _then(_value.copyWith( - defaultDuration: null == defaultDuration - ? _value.defaultDuration - : defaultDuration // ignore: cast_nullable_to_non_nullable - as Duration, - presetDurations: null == presetDurations - ? _value.presetDurations - : presetDurations // ignore: cast_nullable_to_non_nullable - as List, - maxDuration: null == maxDuration - ? _value.maxDuration - : maxDuration // ignore: cast_nullable_to_non_nullable - as Duration, - fadeOutAudio: null == fadeOutAudio - ? _value.fadeOutAudio - : fadeOutAudio // ignore: cast_nullable_to_non_nullable - as bool, - fadeOutDuration: null == fadeOutDuration - ? _value.fadeOutDuration - : fadeOutDuration // ignore: cast_nullable_to_non_nullable - as Duration, - autoRewindWhenStopped: null == autoRewindWhenStopped - ? _value.autoRewindWhenStopped - : autoRewindWhenStopped // ignore: cast_nullable_to_non_nullable - as bool, - autoRewindDurations: null == autoRewindDurations - ? _value.autoRewindDurations - : autoRewindDurations // ignore: cast_nullable_to_non_nullable - as Map, - autoTurnOnTimer: null == autoTurnOnTimer - ? _value.autoTurnOnTimer - : autoTurnOnTimer // ignore: cast_nullable_to_non_nullable - as bool, - alwaysAutoTurnOnTimer: null == alwaysAutoTurnOnTimer - ? _value.alwaysAutoTurnOnTimer - : alwaysAutoTurnOnTimer // ignore: cast_nullable_to_non_nullable - as bool, - autoTurnOnTime: null == autoTurnOnTime - ? _value.autoTurnOnTime - : autoTurnOnTime // ignore: cast_nullable_to_non_nullable - as Duration, - autoTurnOffTime: null == autoTurnOffTime - ? _value.autoTurnOffTime - : autoTurnOffTime // ignore: cast_nullable_to_non_nullable - as Duration, - ) as $Val); - } +/// Create a copy of SleepTimerSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? defaultDuration = null,Object? presetDurations = null,Object? maxDuration = null,Object? fadeOutAudio = null,Object? fadeOutDuration = null,Object? autoRewindWhenStopped = null,Object? autoRewindDurations = null,Object? autoTurnOnTimer = null,Object? alwaysAutoTurnOnTimer = null,Object? autoTurnOnTime = null,Object? autoTurnOffTime = null,}) { + return _then(_self.copyWith( +defaultDuration: null == defaultDuration ? _self.defaultDuration : defaultDuration // ignore: cast_nullable_to_non_nullable +as Duration,presetDurations: null == presetDurations ? _self.presetDurations : presetDurations // ignore: cast_nullable_to_non_nullable +as List,maxDuration: null == maxDuration ? _self.maxDuration : maxDuration // ignore: cast_nullable_to_non_nullable +as Duration,fadeOutAudio: null == fadeOutAudio ? _self.fadeOutAudio : fadeOutAudio // ignore: cast_nullable_to_non_nullable +as bool,fadeOutDuration: null == fadeOutDuration ? _self.fadeOutDuration : fadeOutDuration // ignore: cast_nullable_to_non_nullable +as Duration,autoRewindWhenStopped: null == autoRewindWhenStopped ? _self.autoRewindWhenStopped : autoRewindWhenStopped // ignore: cast_nullable_to_non_nullable +as bool,autoRewindDurations: null == autoRewindDurations ? _self.autoRewindDurations : autoRewindDurations // ignore: cast_nullable_to_non_nullable +as Map,autoTurnOnTimer: null == autoTurnOnTimer ? _self.autoTurnOnTimer : autoTurnOnTimer // ignore: cast_nullable_to_non_nullable +as bool,alwaysAutoTurnOnTimer: null == alwaysAutoTurnOnTimer ? _self.alwaysAutoTurnOnTimer : alwaysAutoTurnOnTimer // ignore: cast_nullable_to_non_nullable +as bool,autoTurnOnTime: null == autoTurnOnTime ? _self.autoTurnOnTime : autoTurnOnTime // ignore: cast_nullable_to_non_nullable +as Duration,autoTurnOffTime: null == autoTurnOffTime ? _self.autoTurnOffTime : autoTurnOffTime // ignore: cast_nullable_to_non_nullable +as Duration, + )); } -/// @nodoc -abstract class _$$SleepTimerSettingsImplCopyWith<$Res> - implements $SleepTimerSettingsCopyWith<$Res> { - factory _$$SleepTimerSettingsImplCopyWith(_$SleepTimerSettingsImpl value, - $Res Function(_$SleepTimerSettingsImpl) then) = - __$$SleepTimerSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {Duration defaultDuration, - List presetDurations, - Duration maxDuration, - bool fadeOutAudio, - Duration fadeOutDuration, - bool autoRewindWhenStopped, - Map autoRewindDurations, - bool autoTurnOnTimer, - bool alwaysAutoTurnOnTimer, - Duration autoTurnOnTime, - Duration autoTurnOffTime}); } -/// @nodoc -class __$$SleepTimerSettingsImplCopyWithImpl<$Res> - extends _$SleepTimerSettingsCopyWithImpl<$Res, _$SleepTimerSettingsImpl> - implements _$$SleepTimerSettingsImplCopyWith<$Res> { - __$$SleepTimerSettingsImplCopyWithImpl(_$SleepTimerSettingsImpl _value, - $Res Function(_$SleepTimerSettingsImpl) _then) - : super(_value, _then); - /// Create a copy of SleepTimerSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? defaultDuration = null, - Object? presetDurations = null, - Object? maxDuration = null, - Object? fadeOutAudio = null, - Object? fadeOutDuration = null, - Object? autoRewindWhenStopped = null, - Object? autoRewindDurations = null, - Object? autoTurnOnTimer = null, - Object? alwaysAutoTurnOnTimer = null, - Object? autoTurnOnTime = null, - Object? autoTurnOffTime = null, - }) { - return _then(_$SleepTimerSettingsImpl( - defaultDuration: null == defaultDuration - ? _value.defaultDuration - : defaultDuration // ignore: cast_nullable_to_non_nullable - as Duration, - presetDurations: null == presetDurations - ? _value._presetDurations - : presetDurations // ignore: cast_nullable_to_non_nullable - as List, - maxDuration: null == maxDuration - ? _value.maxDuration - : maxDuration // ignore: cast_nullable_to_non_nullable - as Duration, - fadeOutAudio: null == fadeOutAudio - ? _value.fadeOutAudio - : fadeOutAudio // ignore: cast_nullable_to_non_nullable - as bool, - fadeOutDuration: null == fadeOutDuration - ? _value.fadeOutDuration - : fadeOutDuration // ignore: cast_nullable_to_non_nullable - as Duration, - autoRewindWhenStopped: null == autoRewindWhenStopped - ? _value.autoRewindWhenStopped - : autoRewindWhenStopped // ignore: cast_nullable_to_non_nullable - as bool, - autoRewindDurations: null == autoRewindDurations - ? _value._autoRewindDurations - : autoRewindDurations // ignore: cast_nullable_to_non_nullable - as Map, - autoTurnOnTimer: null == autoTurnOnTimer - ? _value.autoTurnOnTimer - : autoTurnOnTimer // ignore: cast_nullable_to_non_nullable - as bool, - alwaysAutoTurnOnTimer: null == alwaysAutoTurnOnTimer - ? _value.alwaysAutoTurnOnTimer - : alwaysAutoTurnOnTimer // ignore: cast_nullable_to_non_nullable - as bool, - autoTurnOnTime: null == autoTurnOnTime - ? _value.autoTurnOnTime - : autoTurnOnTime // ignore: cast_nullable_to_non_nullable - as Duration, - autoTurnOffTime: null == autoTurnOffTime - ? _value.autoTurnOffTime - : autoTurnOffTime // ignore: cast_nullable_to_non_nullable - as Duration, - )); - } +/// Adds pattern-matching-related methods to [SleepTimerSettings]. +extension SleepTimerSettingsPatterns on SleepTimerSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SleepTimerSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SleepTimerSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SleepTimerSettings value) $default,){ +final _that = this; +switch (_that) { +case _SleepTimerSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SleepTimerSettings value)? $default,){ +final _that = this; +switch (_that) { +case _SleepTimerSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( Duration defaultDuration, List presetDurations, Duration maxDuration, bool fadeOutAudio, Duration fadeOutDuration, bool autoRewindWhenStopped, Map autoRewindDurations, bool autoTurnOnTimer, bool alwaysAutoTurnOnTimer, Duration autoTurnOnTime, Duration autoTurnOffTime)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SleepTimerSettings() when $default != null: +return $default(_that.defaultDuration,_that.presetDurations,_that.maxDuration,_that.fadeOutAudio,_that.fadeOutDuration,_that.autoRewindWhenStopped,_that.autoRewindDurations,_that.autoTurnOnTimer,_that.alwaysAutoTurnOnTimer,_that.autoTurnOnTime,_that.autoTurnOffTime);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( Duration defaultDuration, List presetDurations, Duration maxDuration, bool fadeOutAudio, Duration fadeOutDuration, bool autoRewindWhenStopped, Map autoRewindDurations, bool autoTurnOnTimer, bool alwaysAutoTurnOnTimer, Duration autoTurnOnTime, Duration autoTurnOffTime) $default,) {final _that = this; +switch (_that) { +case _SleepTimerSettings(): +return $default(_that.defaultDuration,_that.presetDurations,_that.maxDuration,_that.fadeOutAudio,_that.fadeOutDuration,_that.autoRewindWhenStopped,_that.autoRewindDurations,_that.autoTurnOnTimer,_that.alwaysAutoTurnOnTimer,_that.autoTurnOnTime,_that.autoTurnOffTime);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( Duration defaultDuration, List presetDurations, Duration maxDuration, bool fadeOutAudio, Duration fadeOutDuration, bool autoRewindWhenStopped, Map autoRewindDurations, bool autoTurnOnTimer, bool alwaysAutoTurnOnTimer, Duration autoTurnOnTime, Duration autoTurnOffTime)? $default,) {final _that = this; +switch (_that) { +case _SleepTimerSettings() when $default != null: +return $default(_that.defaultDuration,_that.presetDurations,_that.maxDuration,_that.fadeOutAudio,_that.fadeOutDuration,_that.autoRewindWhenStopped,_that.autoRewindDurations,_that.autoTurnOnTimer,_that.alwaysAutoTurnOnTimer,_that.autoTurnOnTime,_that.autoTurnOffTime);case _: + return null; + +} +} + } /// @nodoc @JsonSerializable() -class _$SleepTimerSettingsImpl implements _SleepTimerSettings { - const _$SleepTimerSettingsImpl( - {this.defaultDuration = const Duration(minutes: 15), - final List presetDurations = const [ - Duration(minutes: 5), - Duration(minutes: 10), - Duration(minutes: 15), - Duration(minutes: 20), - Duration(minutes: 30) - ], - this.maxDuration = const Duration(minutes: 100), - this.fadeOutAudio = false, - this.fadeOutDuration = const Duration(seconds: 20), - this.autoRewindWhenStopped = false, - final Map autoRewindDurations = const { - 5: Duration(seconds: 10), - 15: Duration(seconds: 30), - 45: Duration(seconds: 45), - 60: Duration(minutes: 1), - 120: Duration(minutes: 2) - }, - this.autoTurnOnTimer = false, - this.alwaysAutoTurnOnTimer = false, - this.autoTurnOnTime = const Duration(hours: 22, minutes: 0), - this.autoTurnOffTime = const Duration(hours: 6, minutes: 0)}) - : _presetDurations = presetDurations, - _autoRewindDurations = autoRewindDurations; - factory _$SleepTimerSettingsImpl.fromJson(Map json) => - _$$SleepTimerSettingsImplFromJson(json); +class _SleepTimerSettings implements SleepTimerSettings { + const _SleepTimerSettings({this.defaultDuration = const Duration(minutes: 15), final List presetDurations = const [Duration(minutes: 5), Duration(minutes: 10), Duration(minutes: 15), Duration(minutes: 20), Duration(minutes: 30)], this.maxDuration = const Duration(minutes: 100), this.fadeOutAudio = false, this.fadeOutDuration = const Duration(seconds: 20), this.autoRewindWhenStopped = false, final Map autoRewindDurations = const {5 : Duration(seconds: 10), 15 : Duration(seconds: 30), 45 : Duration(seconds: 45), 60 : Duration(minutes: 1), 120 : Duration(minutes: 2)}, this.autoTurnOnTimer = false, this.alwaysAutoTurnOnTimer = false, this.autoTurnOnTime = const Duration(hours: 22, minutes: 0), this.autoTurnOffTime = const Duration(hours: 6, minutes: 0)}): _presetDurations = presetDurations,_autoRewindDurations = autoRewindDurations; + factory _SleepTimerSettings.fromJson(Map json) => _$SleepTimerSettingsFromJson(json); - @override - @JsonKey() - final Duration defaultDuration; - final List _presetDurations; - @override - @JsonKey() - List get presetDurations { - if (_presetDurations is EqualUnmodifiableListView) return _presetDurations; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_presetDurations); - } - - @override - @JsonKey() - final Duration maxDuration; - @override - @JsonKey() - final bool fadeOutAudio; - @override - @JsonKey() - final Duration fadeOutDuration; - - /// if true, the player will automatically rewind the audio when the sleep timer is stopped - @override - @JsonKey() - final bool autoRewindWhenStopped; - - /// the key is the duration in minutes - final Map _autoRewindDurations; - - /// the key is the duration in minutes - @override - @JsonKey() - Map get autoRewindDurations { - if (_autoRewindDurations is EqualUnmodifiableMapView) - return _autoRewindDurations; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_autoRewindDurations); - } - - /// auto turn on timer settings - @override - @JsonKey() - final bool autoTurnOnTimer; - - /// always auto turn on timer settings or during specific times - @override - @JsonKey() - final bool alwaysAutoTurnOnTimer; - - /// auto timer settings, only used if [alwaysAutoTurnOnTimer] is false - /// - /// duration is the time from 00:00 - @override - @JsonKey() - final Duration autoTurnOnTime; - @override - @JsonKey() - final Duration autoTurnOffTime; - - @override - String toString() { - return 'SleepTimerSettings(defaultDuration: $defaultDuration, presetDurations: $presetDurations, maxDuration: $maxDuration, fadeOutAudio: $fadeOutAudio, fadeOutDuration: $fadeOutDuration, autoRewindWhenStopped: $autoRewindWhenStopped, autoRewindDurations: $autoRewindDurations, autoTurnOnTimer: $autoTurnOnTimer, alwaysAutoTurnOnTimer: $alwaysAutoTurnOnTimer, autoTurnOnTime: $autoTurnOnTime, autoTurnOffTime: $autoTurnOffTime)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$SleepTimerSettingsImpl && - (identical(other.defaultDuration, defaultDuration) || - other.defaultDuration == defaultDuration) && - const DeepCollectionEquality() - .equals(other._presetDurations, _presetDurations) && - (identical(other.maxDuration, maxDuration) || - other.maxDuration == maxDuration) && - (identical(other.fadeOutAudio, fadeOutAudio) || - other.fadeOutAudio == fadeOutAudio) && - (identical(other.fadeOutDuration, fadeOutDuration) || - other.fadeOutDuration == fadeOutDuration) && - (identical(other.autoRewindWhenStopped, autoRewindWhenStopped) || - other.autoRewindWhenStopped == autoRewindWhenStopped) && - const DeepCollectionEquality() - .equals(other._autoRewindDurations, _autoRewindDurations) && - (identical(other.autoTurnOnTimer, autoTurnOnTimer) || - other.autoTurnOnTimer == autoTurnOnTimer) && - (identical(other.alwaysAutoTurnOnTimer, alwaysAutoTurnOnTimer) || - other.alwaysAutoTurnOnTimer == alwaysAutoTurnOnTimer) && - (identical(other.autoTurnOnTime, autoTurnOnTime) || - other.autoTurnOnTime == autoTurnOnTime) && - (identical(other.autoTurnOffTime, autoTurnOffTime) || - other.autoTurnOffTime == autoTurnOffTime)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - defaultDuration, - const DeepCollectionEquality().hash(_presetDurations), - maxDuration, - fadeOutAudio, - fadeOutDuration, - autoRewindWhenStopped, - const DeepCollectionEquality().hash(_autoRewindDurations), - autoTurnOnTimer, - alwaysAutoTurnOnTimer, - autoTurnOnTime, - autoTurnOffTime); - - /// Create a copy of SleepTimerSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$SleepTimerSettingsImplCopyWith<_$SleepTimerSettingsImpl> get copyWith => - __$$SleepTimerSettingsImplCopyWithImpl<_$SleepTimerSettingsImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$SleepTimerSettingsImplToJson( - this, - ); - } +@override@JsonKey() final Duration defaultDuration; + final List _presetDurations; +@override@JsonKey() List get presetDurations { + if (_presetDurations is EqualUnmodifiableListView) return _presetDurations; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_presetDurations); } -abstract class _SleepTimerSettings implements SleepTimerSettings { - const factory _SleepTimerSettings( - {final Duration defaultDuration, - final List presetDurations, - final Duration maxDuration, - final bool fadeOutAudio, - final Duration fadeOutDuration, - final bool autoRewindWhenStopped, - final Map autoRewindDurations, - final bool autoTurnOnTimer, - final bool alwaysAutoTurnOnTimer, - final Duration autoTurnOnTime, - final Duration autoTurnOffTime}) = _$SleepTimerSettingsImpl; - - factory _SleepTimerSettings.fromJson(Map json) = - _$SleepTimerSettingsImpl.fromJson; - - @override - Duration get defaultDuration; - @override - List get presetDurations; - @override - Duration get maxDuration; - @override - bool get fadeOutAudio; - @override - Duration get fadeOutDuration; - - /// if true, the player will automatically rewind the audio when the sleep timer is stopped - @override - bool get autoRewindWhenStopped; - - /// the key is the duration in minutes - @override - Map get autoRewindDurations; - - /// auto turn on timer settings - @override - bool get autoTurnOnTimer; - - /// always auto turn on timer settings or during specific times - @override - bool get alwaysAutoTurnOnTimer; - - /// auto timer settings, only used if [alwaysAutoTurnOnTimer] is false - /// - /// duration is the time from 00:00 - @override - Duration get autoTurnOnTime; - @override - Duration get autoTurnOffTime; - - /// Create a copy of SleepTimerSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$SleepTimerSettingsImplCopyWith<_$SleepTimerSettingsImpl> get copyWith => - throw _privateConstructorUsedError; +@override@JsonKey() final Duration maxDuration; +@override@JsonKey() final bool fadeOutAudio; +@override@JsonKey() final Duration fadeOutDuration; +/// if true, the player will automatically rewind the audio when the sleep timer is stopped +@override@JsonKey() final bool autoRewindWhenStopped; +/// the key is the duration in minutes + final Map _autoRewindDurations; +/// the key is the duration in minutes +@override@JsonKey() Map get autoRewindDurations { + if (_autoRewindDurations is EqualUnmodifiableMapView) return _autoRewindDurations; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_autoRewindDurations); } -DownloadSettings _$DownloadSettingsFromJson(Map json) { - return _DownloadSettings.fromJson(json); +/// auto turn on timer settings +@override@JsonKey() final bool autoTurnOnTimer; +/// always auto turn on timer settings or during specific times +@override@JsonKey() final bool alwaysAutoTurnOnTimer; +/// auto timer settings, only used if [alwaysAutoTurnOnTimer] is false +/// +/// duration is the time from 00:00 +@override@JsonKey() final Duration autoTurnOnTime; +@override@JsonKey() final Duration autoTurnOffTime; + +/// Create a copy of SleepTimerSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SleepTimerSettingsCopyWith<_SleepTimerSettings> get copyWith => __$SleepTimerSettingsCopyWithImpl<_SleepTimerSettings>(this, _$identity); + +@override +Map toJson() { + return _$SleepTimerSettingsToJson(this, ); } +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SleepTimerSettings&&(identical(other.defaultDuration, defaultDuration) || other.defaultDuration == defaultDuration)&&const DeepCollectionEquality().equals(other._presetDurations, _presetDurations)&&(identical(other.maxDuration, maxDuration) || other.maxDuration == maxDuration)&&(identical(other.fadeOutAudio, fadeOutAudio) || other.fadeOutAudio == fadeOutAudio)&&(identical(other.fadeOutDuration, fadeOutDuration) || other.fadeOutDuration == fadeOutDuration)&&(identical(other.autoRewindWhenStopped, autoRewindWhenStopped) || other.autoRewindWhenStopped == autoRewindWhenStopped)&&const DeepCollectionEquality().equals(other._autoRewindDurations, _autoRewindDurations)&&(identical(other.autoTurnOnTimer, autoTurnOnTimer) || other.autoTurnOnTimer == autoTurnOnTimer)&&(identical(other.alwaysAutoTurnOnTimer, alwaysAutoTurnOnTimer) || other.alwaysAutoTurnOnTimer == alwaysAutoTurnOnTimer)&&(identical(other.autoTurnOnTime, autoTurnOnTime) || other.autoTurnOnTime == autoTurnOnTime)&&(identical(other.autoTurnOffTime, autoTurnOffTime) || other.autoTurnOffTime == autoTurnOffTime)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,defaultDuration,const DeepCollectionEquality().hash(_presetDurations),maxDuration,fadeOutAudio,fadeOutDuration,autoRewindWhenStopped,const DeepCollectionEquality().hash(_autoRewindDurations),autoTurnOnTimer,alwaysAutoTurnOnTimer,autoTurnOnTime,autoTurnOffTime); + +@override +String toString() { + return 'SleepTimerSettings(defaultDuration: $defaultDuration, presetDurations: $presetDurations, maxDuration: $maxDuration, fadeOutAudio: $fadeOutAudio, fadeOutDuration: $fadeOutDuration, autoRewindWhenStopped: $autoRewindWhenStopped, autoRewindDurations: $autoRewindDurations, autoTurnOnTimer: $autoTurnOnTimer, alwaysAutoTurnOnTimer: $alwaysAutoTurnOnTimer, autoTurnOnTime: $autoTurnOnTime, autoTurnOffTime: $autoTurnOffTime)'; +} + + +} + +/// @nodoc +abstract mixin class _$SleepTimerSettingsCopyWith<$Res> implements $SleepTimerSettingsCopyWith<$Res> { + factory _$SleepTimerSettingsCopyWith(_SleepTimerSettings value, $Res Function(_SleepTimerSettings) _then) = __$SleepTimerSettingsCopyWithImpl; +@override @useResult +$Res call({ + Duration defaultDuration, List presetDurations, Duration maxDuration, bool fadeOutAudio, Duration fadeOutDuration, bool autoRewindWhenStopped, Map autoRewindDurations, bool autoTurnOnTimer, bool alwaysAutoTurnOnTimer, Duration autoTurnOnTime, Duration autoTurnOffTime +}); + + + + +} +/// @nodoc +class __$SleepTimerSettingsCopyWithImpl<$Res> + implements _$SleepTimerSettingsCopyWith<$Res> { + __$SleepTimerSettingsCopyWithImpl(this._self, this._then); + + final _SleepTimerSettings _self; + final $Res Function(_SleepTimerSettings) _then; + +/// Create a copy of SleepTimerSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? defaultDuration = null,Object? presetDurations = null,Object? maxDuration = null,Object? fadeOutAudio = null,Object? fadeOutDuration = null,Object? autoRewindWhenStopped = null,Object? autoRewindDurations = null,Object? autoTurnOnTimer = null,Object? alwaysAutoTurnOnTimer = null,Object? autoTurnOnTime = null,Object? autoTurnOffTime = null,}) { + return _then(_SleepTimerSettings( +defaultDuration: null == defaultDuration ? _self.defaultDuration : defaultDuration // ignore: cast_nullable_to_non_nullable +as Duration,presetDurations: null == presetDurations ? _self._presetDurations : presetDurations // ignore: cast_nullable_to_non_nullable +as List,maxDuration: null == maxDuration ? _self.maxDuration : maxDuration // ignore: cast_nullable_to_non_nullable +as Duration,fadeOutAudio: null == fadeOutAudio ? _self.fadeOutAudio : fadeOutAudio // ignore: cast_nullable_to_non_nullable +as bool,fadeOutDuration: null == fadeOutDuration ? _self.fadeOutDuration : fadeOutDuration // ignore: cast_nullable_to_non_nullable +as Duration,autoRewindWhenStopped: null == autoRewindWhenStopped ? _self.autoRewindWhenStopped : autoRewindWhenStopped // ignore: cast_nullable_to_non_nullable +as bool,autoRewindDurations: null == autoRewindDurations ? _self._autoRewindDurations : autoRewindDurations // ignore: cast_nullable_to_non_nullable +as Map,autoTurnOnTimer: null == autoTurnOnTimer ? _self.autoTurnOnTimer : autoTurnOnTimer // ignore: cast_nullable_to_non_nullable +as bool,alwaysAutoTurnOnTimer: null == alwaysAutoTurnOnTimer ? _self.alwaysAutoTurnOnTimer : alwaysAutoTurnOnTimer // ignore: cast_nullable_to_non_nullable +as bool,autoTurnOnTime: null == autoTurnOnTime ? _self.autoTurnOnTime : autoTurnOnTime // ignore: cast_nullable_to_non_nullable +as Duration,autoTurnOffTime: null == autoTurnOffTime ? _self.autoTurnOffTime : autoTurnOffTime // ignore: cast_nullable_to_non_nullable +as Duration, + )); +} + + +} + + /// @nodoc mixin _$DownloadSettings { - bool get requiresWiFi => throw _privateConstructorUsedError; - int get retries => throw _privateConstructorUsedError; - bool get allowPause => throw _privateConstructorUsedError; - int get maxConcurrent => throw _privateConstructorUsedError; - int get maxConcurrentByHost => throw _privateConstructorUsedError; - int get maxConcurrentByGroup => throw _privateConstructorUsedError; + + bool get requiresWiFi; int get retries; bool get allowPause; int get maxConcurrent; int get maxConcurrentByHost; int get maxConcurrentByGroup; +/// Create a copy of DownloadSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$DownloadSettingsCopyWith get copyWith => _$DownloadSettingsCopyWithImpl(this as DownloadSettings, _$identity); /// Serializes this DownloadSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is DownloadSettings&&(identical(other.requiresWiFi, requiresWiFi) || other.requiresWiFi == requiresWiFi)&&(identical(other.retries, retries) || other.retries == retries)&&(identical(other.allowPause, allowPause) || other.allowPause == allowPause)&&(identical(other.maxConcurrent, maxConcurrent) || other.maxConcurrent == maxConcurrent)&&(identical(other.maxConcurrentByHost, maxConcurrentByHost) || other.maxConcurrentByHost == maxConcurrentByHost)&&(identical(other.maxConcurrentByGroup, maxConcurrentByGroup) || other.maxConcurrentByGroup == maxConcurrentByGroup)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,requiresWiFi,retries,allowPause,maxConcurrent,maxConcurrentByHost,maxConcurrentByGroup); + +@override +String toString() { + return 'DownloadSettings(requiresWiFi: $requiresWiFi, retries: $retries, allowPause: $allowPause, maxConcurrent: $maxConcurrent, maxConcurrentByHost: $maxConcurrentByHost, maxConcurrentByGroup: $maxConcurrentByGroup)'; +} + - /// Create a copy of DownloadSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $DownloadSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $DownloadSettingsCopyWith<$Res> { - factory $DownloadSettingsCopyWith( - DownloadSettings value, $Res Function(DownloadSettings) then) = - _$DownloadSettingsCopyWithImpl<$Res, DownloadSettings>; - @useResult - $Res call( - {bool requiresWiFi, - int retries, - bool allowPause, - int maxConcurrent, - int maxConcurrentByHost, - int maxConcurrentByGroup}); -} +abstract mixin class $DownloadSettingsCopyWith<$Res> { + factory $DownloadSettingsCopyWith(DownloadSettings value, $Res Function(DownloadSettings) _then) = _$DownloadSettingsCopyWithImpl; +@useResult +$Res call({ + bool requiresWiFi, int retries, bool allowPause, int maxConcurrent, int maxConcurrentByHost, int maxConcurrentByGroup +}); + + + +} /// @nodoc -class _$DownloadSettingsCopyWithImpl<$Res, $Val extends DownloadSettings> +class _$DownloadSettingsCopyWithImpl<$Res> implements $DownloadSettingsCopyWith<$Res> { - _$DownloadSettingsCopyWithImpl(this._value, this._then); + _$DownloadSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final DownloadSettings _self; + final $Res Function(DownloadSettings) _then; - /// Create a copy of DownloadSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? requiresWiFi = null, - Object? retries = null, - Object? allowPause = null, - Object? maxConcurrent = null, - Object? maxConcurrentByHost = null, - Object? maxConcurrentByGroup = null, - }) { - return _then(_value.copyWith( - requiresWiFi: null == requiresWiFi - ? _value.requiresWiFi - : requiresWiFi // ignore: cast_nullable_to_non_nullable - as bool, - retries: null == retries - ? _value.retries - : retries // ignore: cast_nullable_to_non_nullable - as int, - allowPause: null == allowPause - ? _value.allowPause - : allowPause // ignore: cast_nullable_to_non_nullable - as bool, - maxConcurrent: null == maxConcurrent - ? _value.maxConcurrent - : maxConcurrent // ignore: cast_nullable_to_non_nullable - as int, - maxConcurrentByHost: null == maxConcurrentByHost - ? _value.maxConcurrentByHost - : maxConcurrentByHost // ignore: cast_nullable_to_non_nullable - as int, - maxConcurrentByGroup: null == maxConcurrentByGroup - ? _value.maxConcurrentByGroup - : maxConcurrentByGroup // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } +/// Create a copy of DownloadSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? requiresWiFi = null,Object? retries = null,Object? allowPause = null,Object? maxConcurrent = null,Object? maxConcurrentByHost = null,Object? maxConcurrentByGroup = null,}) { + return _then(_self.copyWith( +requiresWiFi: null == requiresWiFi ? _self.requiresWiFi : requiresWiFi // ignore: cast_nullable_to_non_nullable +as bool,retries: null == retries ? _self.retries : retries // ignore: cast_nullable_to_non_nullable +as int,allowPause: null == allowPause ? _self.allowPause : allowPause // ignore: cast_nullable_to_non_nullable +as bool,maxConcurrent: null == maxConcurrent ? _self.maxConcurrent : maxConcurrent // ignore: cast_nullable_to_non_nullable +as int,maxConcurrentByHost: null == maxConcurrentByHost ? _self.maxConcurrentByHost : maxConcurrentByHost // ignore: cast_nullable_to_non_nullable +as int,maxConcurrentByGroup: null == maxConcurrentByGroup ? _self.maxConcurrentByGroup : maxConcurrentByGroup // ignore: cast_nullable_to_non_nullable +as int, + )); } -/// @nodoc -abstract class _$$DownloadSettingsImplCopyWith<$Res> - implements $DownloadSettingsCopyWith<$Res> { - factory _$$DownloadSettingsImplCopyWith(_$DownloadSettingsImpl value, - $Res Function(_$DownloadSettingsImpl) then) = - __$$DownloadSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {bool requiresWiFi, - int retries, - bool allowPause, - int maxConcurrent, - int maxConcurrentByHost, - int maxConcurrentByGroup}); } -/// @nodoc -class __$$DownloadSettingsImplCopyWithImpl<$Res> - extends _$DownloadSettingsCopyWithImpl<$Res, _$DownloadSettingsImpl> - implements _$$DownloadSettingsImplCopyWith<$Res> { - __$$DownloadSettingsImplCopyWithImpl(_$DownloadSettingsImpl _value, - $Res Function(_$DownloadSettingsImpl) _then) - : super(_value, _then); - /// Create a copy of DownloadSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? requiresWiFi = null, - Object? retries = null, - Object? allowPause = null, - Object? maxConcurrent = null, - Object? maxConcurrentByHost = null, - Object? maxConcurrentByGroup = null, - }) { - return _then(_$DownloadSettingsImpl( - requiresWiFi: null == requiresWiFi - ? _value.requiresWiFi - : requiresWiFi // ignore: cast_nullable_to_non_nullable - as bool, - retries: null == retries - ? _value.retries - : retries // ignore: cast_nullable_to_non_nullable - as int, - allowPause: null == allowPause - ? _value.allowPause - : allowPause // ignore: cast_nullable_to_non_nullable - as bool, - maxConcurrent: null == maxConcurrent - ? _value.maxConcurrent - : maxConcurrent // ignore: cast_nullable_to_non_nullable - as int, - maxConcurrentByHost: null == maxConcurrentByHost - ? _value.maxConcurrentByHost - : maxConcurrentByHost // ignore: cast_nullable_to_non_nullable - as int, - maxConcurrentByGroup: null == maxConcurrentByGroup - ? _value.maxConcurrentByGroup - : maxConcurrentByGroup // ignore: cast_nullable_to_non_nullable - as int, - )); - } +/// Adds pattern-matching-related methods to [DownloadSettings]. +extension DownloadSettingsPatterns on DownloadSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _DownloadSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _DownloadSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _DownloadSettings value) $default,){ +final _that = this; +switch (_that) { +case _DownloadSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _DownloadSettings value)? $default,){ +final _that = this; +switch (_that) { +case _DownloadSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool requiresWiFi, int retries, bool allowPause, int maxConcurrent, int maxConcurrentByHost, int maxConcurrentByGroup)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _DownloadSettings() when $default != null: +return $default(_that.requiresWiFi,_that.retries,_that.allowPause,_that.maxConcurrent,_that.maxConcurrentByHost,_that.maxConcurrentByGroup);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool requiresWiFi, int retries, bool allowPause, int maxConcurrent, int maxConcurrentByHost, int maxConcurrentByGroup) $default,) {final _that = this; +switch (_that) { +case _DownloadSettings(): +return $default(_that.requiresWiFi,_that.retries,_that.allowPause,_that.maxConcurrent,_that.maxConcurrentByHost,_that.maxConcurrentByGroup);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool requiresWiFi, int retries, bool allowPause, int maxConcurrent, int maxConcurrentByHost, int maxConcurrentByGroup)? $default,) {final _that = this; +switch (_that) { +case _DownloadSettings() when $default != null: +return $default(_that.requiresWiFi,_that.retries,_that.allowPause,_that.maxConcurrent,_that.maxConcurrentByHost,_that.maxConcurrentByGroup);case _: + return null; + +} +} + } /// @nodoc @JsonSerializable() -class _$DownloadSettingsImpl implements _DownloadSettings { - const _$DownloadSettingsImpl( - {this.requiresWiFi = true, - this.retries = 3, - this.allowPause = true, - this.maxConcurrent = 3, - this.maxConcurrentByHost = 3, - this.maxConcurrentByGroup = 3}); - factory _$DownloadSettingsImpl.fromJson(Map json) => - _$$DownloadSettingsImplFromJson(json); +class _DownloadSettings implements DownloadSettings { + const _DownloadSettings({this.requiresWiFi = true, this.retries = 3, this.allowPause = true, this.maxConcurrent = 3, this.maxConcurrentByHost = 3, this.maxConcurrentByGroup = 3}); + factory _DownloadSettings.fromJson(Map json) => _$DownloadSettingsFromJson(json); - @override - @JsonKey() - final bool requiresWiFi; - @override - @JsonKey() - final int retries; - @override - @JsonKey() - final bool allowPause; - @override - @JsonKey() - final int maxConcurrent; - @override - @JsonKey() - final int maxConcurrentByHost; - @override - @JsonKey() - final int maxConcurrentByGroup; +@override@JsonKey() final bool requiresWiFi; +@override@JsonKey() final int retries; +@override@JsonKey() final bool allowPause; +@override@JsonKey() final int maxConcurrent; +@override@JsonKey() final int maxConcurrentByHost; +@override@JsonKey() final int maxConcurrentByGroup; - @override - String toString() { - return 'DownloadSettings(requiresWiFi: $requiresWiFi, retries: $retries, allowPause: $allowPause, maxConcurrent: $maxConcurrent, maxConcurrentByHost: $maxConcurrentByHost, maxConcurrentByGroup: $maxConcurrentByGroup)'; - } +/// Create a copy of DownloadSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$DownloadSettingsCopyWith<_DownloadSettings> get copyWith => __$DownloadSettingsCopyWithImpl<_DownloadSettings>(this, _$identity); - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$DownloadSettingsImpl && - (identical(other.requiresWiFi, requiresWiFi) || - other.requiresWiFi == requiresWiFi) && - (identical(other.retries, retries) || other.retries == retries) && - (identical(other.allowPause, allowPause) || - other.allowPause == allowPause) && - (identical(other.maxConcurrent, maxConcurrent) || - other.maxConcurrent == maxConcurrent) && - (identical(other.maxConcurrentByHost, maxConcurrentByHost) || - other.maxConcurrentByHost == maxConcurrentByHost) && - (identical(other.maxConcurrentByGroup, maxConcurrentByGroup) || - other.maxConcurrentByGroup == maxConcurrentByGroup)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, requiresWiFi, retries, - allowPause, maxConcurrent, maxConcurrentByHost, maxConcurrentByGroup); - - /// Create a copy of DownloadSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$DownloadSettingsImplCopyWith<_$DownloadSettingsImpl> get copyWith => - __$$DownloadSettingsImplCopyWithImpl<_$DownloadSettingsImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$DownloadSettingsImplToJson( - this, - ); - } +@override +Map toJson() { + return _$DownloadSettingsToJson(this, ); } -abstract class _DownloadSettings implements DownloadSettings { - const factory _DownloadSettings( - {final bool requiresWiFi, - final int retries, - final bool allowPause, - final int maxConcurrent, - final int maxConcurrentByHost, - final int maxConcurrentByGroup}) = _$DownloadSettingsImpl; - - factory _DownloadSettings.fromJson(Map json) = - _$DownloadSettingsImpl.fromJson; - - @override - bool get requiresWiFi; - @override - int get retries; - @override - bool get allowPause; - @override - int get maxConcurrent; - @override - int get maxConcurrentByHost; - @override - int get maxConcurrentByGroup; - - /// Create a copy of DownloadSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$DownloadSettingsImplCopyWith<_$DownloadSettingsImpl> get copyWith => - throw _privateConstructorUsedError; +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _DownloadSettings&&(identical(other.requiresWiFi, requiresWiFi) || other.requiresWiFi == requiresWiFi)&&(identical(other.retries, retries) || other.retries == retries)&&(identical(other.allowPause, allowPause) || other.allowPause == allowPause)&&(identical(other.maxConcurrent, maxConcurrent) || other.maxConcurrent == maxConcurrent)&&(identical(other.maxConcurrentByHost, maxConcurrentByHost) || other.maxConcurrentByHost == maxConcurrentByHost)&&(identical(other.maxConcurrentByGroup, maxConcurrentByGroup) || other.maxConcurrentByGroup == maxConcurrentByGroup)); } -NotificationSettings _$NotificationSettingsFromJson(Map json) { - return _NotificationSettings.fromJson(json); +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,requiresWiFi,retries,allowPause,maxConcurrent,maxConcurrentByHost,maxConcurrentByGroup); + +@override +String toString() { + return 'DownloadSettings(requiresWiFi: $requiresWiFi, retries: $retries, allowPause: $allowPause, maxConcurrent: $maxConcurrent, maxConcurrentByHost: $maxConcurrentByHost, maxConcurrentByGroup: $maxConcurrentByGroup)'; } + +} + +/// @nodoc +abstract mixin class _$DownloadSettingsCopyWith<$Res> implements $DownloadSettingsCopyWith<$Res> { + factory _$DownloadSettingsCopyWith(_DownloadSettings value, $Res Function(_DownloadSettings) _then) = __$DownloadSettingsCopyWithImpl; +@override @useResult +$Res call({ + bool requiresWiFi, int retries, bool allowPause, int maxConcurrent, int maxConcurrentByHost, int maxConcurrentByGroup +}); + + + + +} +/// @nodoc +class __$DownloadSettingsCopyWithImpl<$Res> + implements _$DownloadSettingsCopyWith<$Res> { + __$DownloadSettingsCopyWithImpl(this._self, this._then); + + final _DownloadSettings _self; + final $Res Function(_DownloadSettings) _then; + +/// Create a copy of DownloadSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? requiresWiFi = null,Object? retries = null,Object? allowPause = null,Object? maxConcurrent = null,Object? maxConcurrentByHost = null,Object? maxConcurrentByGroup = null,}) { + return _then(_DownloadSettings( +requiresWiFi: null == requiresWiFi ? _self.requiresWiFi : requiresWiFi // ignore: cast_nullable_to_non_nullable +as bool,retries: null == retries ? _self.retries : retries // ignore: cast_nullable_to_non_nullable +as int,allowPause: null == allowPause ? _self.allowPause : allowPause // ignore: cast_nullable_to_non_nullable +as bool,maxConcurrent: null == maxConcurrent ? _self.maxConcurrent : maxConcurrent // ignore: cast_nullable_to_non_nullable +as int,maxConcurrentByHost: null == maxConcurrentByHost ? _self.maxConcurrentByHost : maxConcurrentByHost // ignore: cast_nullable_to_non_nullable +as int,maxConcurrentByGroup: null == maxConcurrentByGroup ? _self.maxConcurrentByGroup : maxConcurrentByGroup // ignore: cast_nullable_to_non_nullable +as int, + )); +} + + +} + + /// @nodoc mixin _$NotificationSettings { - Duration get fastForwardInterval => throw _privateConstructorUsedError; - Duration get rewindInterval => throw _privateConstructorUsedError; - bool get progressBarIsChapterProgress => throw _privateConstructorUsedError; - String get primaryTitle => throw _privateConstructorUsedError; - String get secondaryTitle => throw _privateConstructorUsedError; - List get mediaControls => - throw _privateConstructorUsedError; + + Duration get fastForwardInterval; Duration get rewindInterval; bool get progressBarIsChapterProgress; String get primaryTitle; String get secondaryTitle; List get mediaControls; +/// Create a copy of NotificationSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$NotificationSettingsCopyWith get copyWith => _$NotificationSettingsCopyWithImpl(this as NotificationSettings, _$identity); /// Serializes this NotificationSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is NotificationSettings&&(identical(other.fastForwardInterval, fastForwardInterval) || other.fastForwardInterval == fastForwardInterval)&&(identical(other.rewindInterval, rewindInterval) || other.rewindInterval == rewindInterval)&&(identical(other.progressBarIsChapterProgress, progressBarIsChapterProgress) || other.progressBarIsChapterProgress == progressBarIsChapterProgress)&&(identical(other.primaryTitle, primaryTitle) || other.primaryTitle == primaryTitle)&&(identical(other.secondaryTitle, secondaryTitle) || other.secondaryTitle == secondaryTitle)&&const DeepCollectionEquality().equals(other.mediaControls, mediaControls)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,fastForwardInterval,rewindInterval,progressBarIsChapterProgress,primaryTitle,secondaryTitle,const DeepCollectionEquality().hash(mediaControls)); + +@override +String toString() { + return 'NotificationSettings(fastForwardInterval: $fastForwardInterval, rewindInterval: $rewindInterval, progressBarIsChapterProgress: $progressBarIsChapterProgress, primaryTitle: $primaryTitle, secondaryTitle: $secondaryTitle, mediaControls: $mediaControls)'; +} + - /// Create a copy of NotificationSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $NotificationSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $NotificationSettingsCopyWith<$Res> { - factory $NotificationSettingsCopyWith(NotificationSettings value, - $Res Function(NotificationSettings) then) = - _$NotificationSettingsCopyWithImpl<$Res, NotificationSettings>; - @useResult - $Res call( - {Duration fastForwardInterval, - Duration rewindInterval, - bool progressBarIsChapterProgress, - String primaryTitle, - String secondaryTitle, - List mediaControls}); -} +abstract mixin class $NotificationSettingsCopyWith<$Res> { + factory $NotificationSettingsCopyWith(NotificationSettings value, $Res Function(NotificationSettings) _then) = _$NotificationSettingsCopyWithImpl; +@useResult +$Res call({ + Duration fastForwardInterval, Duration rewindInterval, bool progressBarIsChapterProgress, String primaryTitle, String secondaryTitle, List mediaControls +}); + + + +} /// @nodoc -class _$NotificationSettingsCopyWithImpl<$Res, - $Val extends NotificationSettings> +class _$NotificationSettingsCopyWithImpl<$Res> implements $NotificationSettingsCopyWith<$Res> { - _$NotificationSettingsCopyWithImpl(this._value, this._then); + _$NotificationSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final NotificationSettings _self; + final $Res Function(NotificationSettings) _then; - /// Create a copy of NotificationSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? fastForwardInterval = null, - Object? rewindInterval = null, - Object? progressBarIsChapterProgress = null, - Object? primaryTitle = null, - Object? secondaryTitle = null, - Object? mediaControls = null, - }) { - return _then(_value.copyWith( - fastForwardInterval: null == fastForwardInterval - ? _value.fastForwardInterval - : fastForwardInterval // ignore: cast_nullable_to_non_nullable - as Duration, - rewindInterval: null == rewindInterval - ? _value.rewindInterval - : rewindInterval // ignore: cast_nullable_to_non_nullable - as Duration, - progressBarIsChapterProgress: null == progressBarIsChapterProgress - ? _value.progressBarIsChapterProgress - : progressBarIsChapterProgress // ignore: cast_nullable_to_non_nullable - as bool, - primaryTitle: null == primaryTitle - ? _value.primaryTitle - : primaryTitle // ignore: cast_nullable_to_non_nullable - as String, - secondaryTitle: null == secondaryTitle - ? _value.secondaryTitle - : secondaryTitle // ignore: cast_nullable_to_non_nullable - as String, - mediaControls: null == mediaControls - ? _value.mediaControls - : mediaControls // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } +/// Create a copy of NotificationSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? fastForwardInterval = null,Object? rewindInterval = null,Object? progressBarIsChapterProgress = null,Object? primaryTitle = null,Object? secondaryTitle = null,Object? mediaControls = null,}) { + return _then(_self.copyWith( +fastForwardInterval: null == fastForwardInterval ? _self.fastForwardInterval : fastForwardInterval // ignore: cast_nullable_to_non_nullable +as Duration,rewindInterval: null == rewindInterval ? _self.rewindInterval : rewindInterval // ignore: cast_nullable_to_non_nullable +as Duration,progressBarIsChapterProgress: null == progressBarIsChapterProgress ? _self.progressBarIsChapterProgress : progressBarIsChapterProgress // ignore: cast_nullable_to_non_nullable +as bool,primaryTitle: null == primaryTitle ? _self.primaryTitle : primaryTitle // ignore: cast_nullable_to_non_nullable +as String,secondaryTitle: null == secondaryTitle ? _self.secondaryTitle : secondaryTitle // ignore: cast_nullable_to_non_nullable +as String,mediaControls: null == mediaControls ? _self.mediaControls : mediaControls // ignore: cast_nullable_to_non_nullable +as List, + )); } -/// @nodoc -abstract class _$$NotificationSettingsImplCopyWith<$Res> - implements $NotificationSettingsCopyWith<$Res> { - factory _$$NotificationSettingsImplCopyWith(_$NotificationSettingsImpl value, - $Res Function(_$NotificationSettingsImpl) then) = - __$$NotificationSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {Duration fastForwardInterval, - Duration rewindInterval, - bool progressBarIsChapterProgress, - String primaryTitle, - String secondaryTitle, - List mediaControls}); } -/// @nodoc -class __$$NotificationSettingsImplCopyWithImpl<$Res> - extends _$NotificationSettingsCopyWithImpl<$Res, _$NotificationSettingsImpl> - implements _$$NotificationSettingsImplCopyWith<$Res> { - __$$NotificationSettingsImplCopyWithImpl(_$NotificationSettingsImpl _value, - $Res Function(_$NotificationSettingsImpl) _then) - : super(_value, _then); - /// Create a copy of NotificationSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? fastForwardInterval = null, - Object? rewindInterval = null, - Object? progressBarIsChapterProgress = null, - Object? primaryTitle = null, - Object? secondaryTitle = null, - Object? mediaControls = null, - }) { - return _then(_$NotificationSettingsImpl( - fastForwardInterval: null == fastForwardInterval - ? _value.fastForwardInterval - : fastForwardInterval // ignore: cast_nullable_to_non_nullable - as Duration, - rewindInterval: null == rewindInterval - ? _value.rewindInterval - : rewindInterval // ignore: cast_nullable_to_non_nullable - as Duration, - progressBarIsChapterProgress: null == progressBarIsChapterProgress - ? _value.progressBarIsChapterProgress - : progressBarIsChapterProgress // ignore: cast_nullable_to_non_nullable - as bool, - primaryTitle: null == primaryTitle - ? _value.primaryTitle - : primaryTitle // ignore: cast_nullable_to_non_nullable - as String, - secondaryTitle: null == secondaryTitle - ? _value.secondaryTitle - : secondaryTitle // ignore: cast_nullable_to_non_nullable - as String, - mediaControls: null == mediaControls - ? _value._mediaControls - : mediaControls // ignore: cast_nullable_to_non_nullable - as List, - )); - } +/// Adds pattern-matching-related methods to [NotificationSettings]. +extension NotificationSettingsPatterns on NotificationSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _NotificationSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _NotificationSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _NotificationSettings value) $default,){ +final _that = this; +switch (_that) { +case _NotificationSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _NotificationSettings value)? $default,){ +final _that = this; +switch (_that) { +case _NotificationSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( Duration fastForwardInterval, Duration rewindInterval, bool progressBarIsChapterProgress, String primaryTitle, String secondaryTitle, List mediaControls)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _NotificationSettings() when $default != null: +return $default(_that.fastForwardInterval,_that.rewindInterval,_that.progressBarIsChapterProgress,_that.primaryTitle,_that.secondaryTitle,_that.mediaControls);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( Duration fastForwardInterval, Duration rewindInterval, bool progressBarIsChapterProgress, String primaryTitle, String secondaryTitle, List mediaControls) $default,) {final _that = this; +switch (_that) { +case _NotificationSettings(): +return $default(_that.fastForwardInterval,_that.rewindInterval,_that.progressBarIsChapterProgress,_that.primaryTitle,_that.secondaryTitle,_that.mediaControls);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( Duration fastForwardInterval, Duration rewindInterval, bool progressBarIsChapterProgress, String primaryTitle, String secondaryTitle, List mediaControls)? $default,) {final _that = this; +switch (_that) { +case _NotificationSettings() when $default != null: +return $default(_that.fastForwardInterval,_that.rewindInterval,_that.progressBarIsChapterProgress,_that.primaryTitle,_that.secondaryTitle,_that.mediaControls);case _: + return null; + +} +} + } /// @nodoc @JsonSerializable() -class _$NotificationSettingsImpl implements _NotificationSettings { - const _$NotificationSettingsImpl( - {this.fastForwardInterval = const Duration(seconds: 30), - this.rewindInterval = const Duration(seconds: 10), - this.progressBarIsChapterProgress = true, - this.primaryTitle = '\$bookTitle', - this.secondaryTitle = '\$author', - final List mediaControls = const [ - NotificationMediaControl.rewind, - NotificationMediaControl.fastForward, - NotificationMediaControl.skipToPreviousChapter, - NotificationMediaControl.skipToNextChapter - ]}) - : _mediaControls = mediaControls; - factory _$NotificationSettingsImpl.fromJson(Map json) => - _$$NotificationSettingsImplFromJson(json); +class _NotificationSettings implements NotificationSettings { + const _NotificationSettings({this.fastForwardInterval = const Duration(seconds: 30), this.rewindInterval = const Duration(seconds: 10), this.progressBarIsChapterProgress = true, this.primaryTitle = '\$bookTitle', this.secondaryTitle = '\$author', final List mediaControls = const [NotificationMediaControl.rewind, NotificationMediaControl.fastForward, NotificationMediaControl.skipToPreviousChapter, NotificationMediaControl.skipToNextChapter]}): _mediaControls = mediaControls; + factory _NotificationSettings.fromJson(Map json) => _$NotificationSettingsFromJson(json); - @override - @JsonKey() - final Duration fastForwardInterval; - @override - @JsonKey() - final Duration rewindInterval; - @override - @JsonKey() - final bool progressBarIsChapterProgress; - @override - @JsonKey() - final String primaryTitle; - @override - @JsonKey() - final String secondaryTitle; - final List _mediaControls; - @override - @JsonKey() - List get mediaControls { - if (_mediaControls is EqualUnmodifiableListView) return _mediaControls; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_mediaControls); - } - - @override - String toString() { - return 'NotificationSettings(fastForwardInterval: $fastForwardInterval, rewindInterval: $rewindInterval, progressBarIsChapterProgress: $progressBarIsChapterProgress, primaryTitle: $primaryTitle, secondaryTitle: $secondaryTitle, mediaControls: $mediaControls)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$NotificationSettingsImpl && - (identical(other.fastForwardInterval, fastForwardInterval) || - other.fastForwardInterval == fastForwardInterval) && - (identical(other.rewindInterval, rewindInterval) || - other.rewindInterval == rewindInterval) && - (identical(other.progressBarIsChapterProgress, - progressBarIsChapterProgress) || - other.progressBarIsChapterProgress == - progressBarIsChapterProgress) && - (identical(other.primaryTitle, primaryTitle) || - other.primaryTitle == primaryTitle) && - (identical(other.secondaryTitle, secondaryTitle) || - other.secondaryTitle == secondaryTitle) && - const DeepCollectionEquality() - .equals(other._mediaControls, _mediaControls)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - fastForwardInterval, - rewindInterval, - progressBarIsChapterProgress, - primaryTitle, - secondaryTitle, - const DeepCollectionEquality().hash(_mediaControls)); - - /// Create a copy of NotificationSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$NotificationSettingsImplCopyWith<_$NotificationSettingsImpl> - get copyWith => - __$$NotificationSettingsImplCopyWithImpl<_$NotificationSettingsImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$NotificationSettingsImplToJson( - this, - ); - } +@override@JsonKey() final Duration fastForwardInterval; +@override@JsonKey() final Duration rewindInterval; +@override@JsonKey() final bool progressBarIsChapterProgress; +@override@JsonKey() final String primaryTitle; +@override@JsonKey() final String secondaryTitle; + final List _mediaControls; +@override@JsonKey() List get mediaControls { + if (_mediaControls is EqualUnmodifiableListView) return _mediaControls; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_mediaControls); } -abstract class _NotificationSettings implements NotificationSettings { - const factory _NotificationSettings( - {final Duration fastForwardInterval, - final Duration rewindInterval, - final bool progressBarIsChapterProgress, - final String primaryTitle, - final String secondaryTitle, - final List mediaControls}) = - _$NotificationSettingsImpl; - factory _NotificationSettings.fromJson(Map json) = - _$NotificationSettingsImpl.fromJson; +/// Create a copy of NotificationSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$NotificationSettingsCopyWith<_NotificationSettings> get copyWith => __$NotificationSettingsCopyWithImpl<_NotificationSettings>(this, _$identity); - @override - Duration get fastForwardInterval; - @override - Duration get rewindInterval; - @override - bool get progressBarIsChapterProgress; - @override - String get primaryTitle; - @override - String get secondaryTitle; - @override - List get mediaControls; - - /// Create a copy of NotificationSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$NotificationSettingsImplCopyWith<_$NotificationSettingsImpl> - get copyWith => throw _privateConstructorUsedError; +@override +Map toJson() { + return _$NotificationSettingsToJson(this, ); } -ShakeDetectionSettings _$ShakeDetectionSettingsFromJson( - Map json) { - return _ShakeDetectionSettings.fromJson(json); +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _NotificationSettings&&(identical(other.fastForwardInterval, fastForwardInterval) || other.fastForwardInterval == fastForwardInterval)&&(identical(other.rewindInterval, rewindInterval) || other.rewindInterval == rewindInterval)&&(identical(other.progressBarIsChapterProgress, progressBarIsChapterProgress) || other.progressBarIsChapterProgress == progressBarIsChapterProgress)&&(identical(other.primaryTitle, primaryTitle) || other.primaryTitle == primaryTitle)&&(identical(other.secondaryTitle, secondaryTitle) || other.secondaryTitle == secondaryTitle)&&const DeepCollectionEquality().equals(other._mediaControls, _mediaControls)); } +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,fastForwardInterval,rewindInterval,progressBarIsChapterProgress,primaryTitle,secondaryTitle,const DeepCollectionEquality().hash(_mediaControls)); + +@override +String toString() { + return 'NotificationSettings(fastForwardInterval: $fastForwardInterval, rewindInterval: $rewindInterval, progressBarIsChapterProgress: $progressBarIsChapterProgress, primaryTitle: $primaryTitle, secondaryTitle: $secondaryTitle, mediaControls: $mediaControls)'; +} + + +} + +/// @nodoc +abstract mixin class _$NotificationSettingsCopyWith<$Res> implements $NotificationSettingsCopyWith<$Res> { + factory _$NotificationSettingsCopyWith(_NotificationSettings value, $Res Function(_NotificationSettings) _then) = __$NotificationSettingsCopyWithImpl; +@override @useResult +$Res call({ + Duration fastForwardInterval, Duration rewindInterval, bool progressBarIsChapterProgress, String primaryTitle, String secondaryTitle, List mediaControls +}); + + + + +} +/// @nodoc +class __$NotificationSettingsCopyWithImpl<$Res> + implements _$NotificationSettingsCopyWith<$Res> { + __$NotificationSettingsCopyWithImpl(this._self, this._then); + + final _NotificationSettings _self; + final $Res Function(_NotificationSettings) _then; + +/// Create a copy of NotificationSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? fastForwardInterval = null,Object? rewindInterval = null,Object? progressBarIsChapterProgress = null,Object? primaryTitle = null,Object? secondaryTitle = null,Object? mediaControls = null,}) { + return _then(_NotificationSettings( +fastForwardInterval: null == fastForwardInterval ? _self.fastForwardInterval : fastForwardInterval // ignore: cast_nullable_to_non_nullable +as Duration,rewindInterval: null == rewindInterval ? _self.rewindInterval : rewindInterval // ignore: cast_nullable_to_non_nullable +as Duration,progressBarIsChapterProgress: null == progressBarIsChapterProgress ? _self.progressBarIsChapterProgress : progressBarIsChapterProgress // ignore: cast_nullable_to_non_nullable +as bool,primaryTitle: null == primaryTitle ? _self.primaryTitle : primaryTitle // ignore: cast_nullable_to_non_nullable +as String,secondaryTitle: null == secondaryTitle ? _self.secondaryTitle : secondaryTitle // ignore: cast_nullable_to_non_nullable +as String,mediaControls: null == mediaControls ? _self._mediaControls : mediaControls // ignore: cast_nullable_to_non_nullable +as List, + )); +} + + +} + + /// @nodoc mixin _$ShakeDetectionSettings { - bool get isEnabled => throw _privateConstructorUsedError; - ShakeDirection get direction => throw _privateConstructorUsedError; - double get threshold => throw _privateConstructorUsedError; - ShakeAction get shakeAction => throw _privateConstructorUsedError; - Set get feedback => throw _privateConstructorUsedError; - double get beepVolume => throw _privateConstructorUsedError; - /// the duration to wait before the shake detection is enabled again - Duration get shakeTriggerCoolDown => throw _privateConstructorUsedError; - - /// the number of shakes required to trigger the action - int get shakeTriggerCount => throw _privateConstructorUsedError; - - /// acceleration sampling interval - Duration get samplingPeriod => throw _privateConstructorUsedError; + bool get isEnabled; ShakeDirection get direction; double get threshold; ShakeAction get shakeAction; Set get feedback; double get beepVolume;/// the duration to wait before the shake detection is enabled again + Duration get shakeTriggerCoolDown;/// the number of shakes required to trigger the action + int get shakeTriggerCount;/// acceleration sampling interval + Duration get samplingPeriod; +/// Create a copy of ShakeDetectionSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ShakeDetectionSettingsCopyWith get copyWith => _$ShakeDetectionSettingsCopyWithImpl(this as ShakeDetectionSettings, _$identity); /// Serializes this ShakeDetectionSettings to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ShakeDetectionSettings&&(identical(other.isEnabled, isEnabled) || other.isEnabled == isEnabled)&&(identical(other.direction, direction) || other.direction == direction)&&(identical(other.threshold, threshold) || other.threshold == threshold)&&(identical(other.shakeAction, shakeAction) || other.shakeAction == shakeAction)&&const DeepCollectionEquality().equals(other.feedback, feedback)&&(identical(other.beepVolume, beepVolume) || other.beepVolume == beepVolume)&&(identical(other.shakeTriggerCoolDown, shakeTriggerCoolDown) || other.shakeTriggerCoolDown == shakeTriggerCoolDown)&&(identical(other.shakeTriggerCount, shakeTriggerCount) || other.shakeTriggerCount == shakeTriggerCount)&&(identical(other.samplingPeriod, samplingPeriod) || other.samplingPeriod == samplingPeriod)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,isEnabled,direction,threshold,shakeAction,const DeepCollectionEquality().hash(feedback),beepVolume,shakeTriggerCoolDown,shakeTriggerCount,samplingPeriod); + +@override +String toString() { + return 'ShakeDetectionSettings(isEnabled: $isEnabled, direction: $direction, threshold: $threshold, shakeAction: $shakeAction, feedback: $feedback, beepVolume: $beepVolume, shakeTriggerCoolDown: $shakeTriggerCoolDown, shakeTriggerCount: $shakeTriggerCount, samplingPeriod: $samplingPeriod)'; +} + - /// Create a copy of ShakeDetectionSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ShakeDetectionSettingsCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $ShakeDetectionSettingsCopyWith<$Res> { - factory $ShakeDetectionSettingsCopyWith(ShakeDetectionSettings value, - $Res Function(ShakeDetectionSettings) then) = - _$ShakeDetectionSettingsCopyWithImpl<$Res, ShakeDetectionSettings>; - @useResult - $Res call( - {bool isEnabled, - ShakeDirection direction, - double threshold, - ShakeAction shakeAction, - Set feedback, - double beepVolume, - Duration shakeTriggerCoolDown, - int shakeTriggerCount, - Duration samplingPeriod}); -} +abstract mixin class $ShakeDetectionSettingsCopyWith<$Res> { + factory $ShakeDetectionSettingsCopyWith(ShakeDetectionSettings value, $Res Function(ShakeDetectionSettings) _then) = _$ShakeDetectionSettingsCopyWithImpl; +@useResult +$Res call({ + bool isEnabled, ShakeDirection direction, double threshold, ShakeAction shakeAction, Set feedback, double beepVolume, Duration shakeTriggerCoolDown, int shakeTriggerCount, Duration samplingPeriod +}); + + + +} /// @nodoc -class _$ShakeDetectionSettingsCopyWithImpl<$Res, - $Val extends ShakeDetectionSettings> +class _$ShakeDetectionSettingsCopyWithImpl<$Res> implements $ShakeDetectionSettingsCopyWith<$Res> { - _$ShakeDetectionSettingsCopyWithImpl(this._value, this._then); + _$ShakeDetectionSettingsCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final ShakeDetectionSettings _self; + final $Res Function(ShakeDetectionSettings) _then; - /// Create a copy of ShakeDetectionSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? isEnabled = null, - Object? direction = null, - Object? threshold = null, - Object? shakeAction = null, - Object? feedback = null, - Object? beepVolume = null, - Object? shakeTriggerCoolDown = null, - Object? shakeTriggerCount = null, - Object? samplingPeriod = null, - }) { - return _then(_value.copyWith( - isEnabled: null == isEnabled - ? _value.isEnabled - : isEnabled // ignore: cast_nullable_to_non_nullable - as bool, - direction: null == direction - ? _value.direction - : direction // ignore: cast_nullable_to_non_nullable - as ShakeDirection, - threshold: null == threshold - ? _value.threshold - : threshold // ignore: cast_nullable_to_non_nullable - as double, - shakeAction: null == shakeAction - ? _value.shakeAction - : shakeAction // ignore: cast_nullable_to_non_nullable - as ShakeAction, - feedback: null == feedback - ? _value.feedback - : feedback // ignore: cast_nullable_to_non_nullable - as Set, - beepVolume: null == beepVolume - ? _value.beepVolume - : beepVolume // ignore: cast_nullable_to_non_nullable - as double, - shakeTriggerCoolDown: null == shakeTriggerCoolDown - ? _value.shakeTriggerCoolDown - : shakeTriggerCoolDown // ignore: cast_nullable_to_non_nullable - as Duration, - shakeTriggerCount: null == shakeTriggerCount - ? _value.shakeTriggerCount - : shakeTriggerCount // ignore: cast_nullable_to_non_nullable - as int, - samplingPeriod: null == samplingPeriod - ? _value.samplingPeriod - : samplingPeriod // ignore: cast_nullable_to_non_nullable - as Duration, - ) as $Val); - } +/// Create a copy of ShakeDetectionSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? isEnabled = null,Object? direction = null,Object? threshold = null,Object? shakeAction = null,Object? feedback = null,Object? beepVolume = null,Object? shakeTriggerCoolDown = null,Object? shakeTriggerCount = null,Object? samplingPeriod = null,}) { + return _then(_self.copyWith( +isEnabled: null == isEnabled ? _self.isEnabled : isEnabled // ignore: cast_nullable_to_non_nullable +as bool,direction: null == direction ? _self.direction : direction // ignore: cast_nullable_to_non_nullable +as ShakeDirection,threshold: null == threshold ? _self.threshold : threshold // ignore: cast_nullable_to_non_nullable +as double,shakeAction: null == shakeAction ? _self.shakeAction : shakeAction // ignore: cast_nullable_to_non_nullable +as ShakeAction,feedback: null == feedback ? _self.feedback : feedback // ignore: cast_nullable_to_non_nullable +as Set,beepVolume: null == beepVolume ? _self.beepVolume : beepVolume // ignore: cast_nullable_to_non_nullable +as double,shakeTriggerCoolDown: null == shakeTriggerCoolDown ? _self.shakeTriggerCoolDown : shakeTriggerCoolDown // ignore: cast_nullable_to_non_nullable +as Duration,shakeTriggerCount: null == shakeTriggerCount ? _self.shakeTriggerCount : shakeTriggerCount // ignore: cast_nullable_to_non_nullable +as int,samplingPeriod: null == samplingPeriod ? _self.samplingPeriod : samplingPeriod // ignore: cast_nullable_to_non_nullable +as Duration, + )); } -/// @nodoc -abstract class _$$ShakeDetectionSettingsImplCopyWith<$Res> - implements $ShakeDetectionSettingsCopyWith<$Res> { - factory _$$ShakeDetectionSettingsImplCopyWith( - _$ShakeDetectionSettingsImpl value, - $Res Function(_$ShakeDetectionSettingsImpl) then) = - __$$ShakeDetectionSettingsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {bool isEnabled, - ShakeDirection direction, - double threshold, - ShakeAction shakeAction, - Set feedback, - double beepVolume, - Duration shakeTriggerCoolDown, - int shakeTriggerCount, - Duration samplingPeriod}); } -/// @nodoc -class __$$ShakeDetectionSettingsImplCopyWithImpl<$Res> - extends _$ShakeDetectionSettingsCopyWithImpl<$Res, - _$ShakeDetectionSettingsImpl> - implements _$$ShakeDetectionSettingsImplCopyWith<$Res> { - __$$ShakeDetectionSettingsImplCopyWithImpl( - _$ShakeDetectionSettingsImpl _value, - $Res Function(_$ShakeDetectionSettingsImpl) _then) - : super(_value, _then); - /// Create a copy of ShakeDetectionSettings - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? isEnabled = null, - Object? direction = null, - Object? threshold = null, - Object? shakeAction = null, - Object? feedback = null, - Object? beepVolume = null, - Object? shakeTriggerCoolDown = null, - Object? shakeTriggerCount = null, - Object? samplingPeriod = null, - }) { - return _then(_$ShakeDetectionSettingsImpl( - isEnabled: null == isEnabled - ? _value.isEnabled - : isEnabled // ignore: cast_nullable_to_non_nullable - as bool, - direction: null == direction - ? _value.direction - : direction // ignore: cast_nullable_to_non_nullable - as ShakeDirection, - threshold: null == threshold - ? _value.threshold - : threshold // ignore: cast_nullable_to_non_nullable - as double, - shakeAction: null == shakeAction - ? _value.shakeAction - : shakeAction // ignore: cast_nullable_to_non_nullable - as ShakeAction, - feedback: null == feedback - ? _value._feedback - : feedback // ignore: cast_nullable_to_non_nullable - as Set, - beepVolume: null == beepVolume - ? _value.beepVolume - : beepVolume // ignore: cast_nullable_to_non_nullable - as double, - shakeTriggerCoolDown: null == shakeTriggerCoolDown - ? _value.shakeTriggerCoolDown - : shakeTriggerCoolDown // ignore: cast_nullable_to_non_nullable - as Duration, - shakeTriggerCount: null == shakeTriggerCount - ? _value.shakeTriggerCount - : shakeTriggerCount // ignore: cast_nullable_to_non_nullable - as int, - samplingPeriod: null == samplingPeriod - ? _value.samplingPeriod - : samplingPeriod // ignore: cast_nullable_to_non_nullable - as Duration, - )); - } +/// Adds pattern-matching-related methods to [ShakeDetectionSettings]. +extension ShakeDetectionSettingsPatterns on ShakeDetectionSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ShakeDetectionSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ShakeDetectionSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ShakeDetectionSettings value) $default,){ +final _that = this; +switch (_that) { +case _ShakeDetectionSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ShakeDetectionSettings value)? $default,){ +final _that = this; +switch (_that) { +case _ShakeDetectionSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool isEnabled, ShakeDirection direction, double threshold, ShakeAction shakeAction, Set feedback, double beepVolume, Duration shakeTriggerCoolDown, int shakeTriggerCount, Duration samplingPeriod)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ShakeDetectionSettings() when $default != null: +return $default(_that.isEnabled,_that.direction,_that.threshold,_that.shakeAction,_that.feedback,_that.beepVolume,_that.shakeTriggerCoolDown,_that.shakeTriggerCount,_that.samplingPeriod);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool isEnabled, ShakeDirection direction, double threshold, ShakeAction shakeAction, Set feedback, double beepVolume, Duration shakeTriggerCoolDown, int shakeTriggerCount, Duration samplingPeriod) $default,) {final _that = this; +switch (_that) { +case _ShakeDetectionSettings(): +return $default(_that.isEnabled,_that.direction,_that.threshold,_that.shakeAction,_that.feedback,_that.beepVolume,_that.shakeTriggerCoolDown,_that.shakeTriggerCount,_that.samplingPeriod);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool isEnabled, ShakeDirection direction, double threshold, ShakeAction shakeAction, Set feedback, double beepVolume, Duration shakeTriggerCoolDown, int shakeTriggerCount, Duration samplingPeriod)? $default,) {final _that = this; +switch (_that) { +case _ShakeDetectionSettings() when $default != null: +return $default(_that.isEnabled,_that.direction,_that.threshold,_that.shakeAction,_that.feedback,_that.beepVolume,_that.shakeTriggerCoolDown,_that.shakeTriggerCount,_that.samplingPeriod);case _: + return null; + +} +} + } /// @nodoc @JsonSerializable() -class _$ShakeDetectionSettingsImpl implements _ShakeDetectionSettings { - const _$ShakeDetectionSettingsImpl( - {this.isEnabled = true, - this.direction = ShakeDirection.horizontal, - this.threshold = 5, - this.shakeAction = ShakeAction.resetSleepTimer, - final Set feedback = const { - ShakeDetectedFeedback.vibrate - }, - this.beepVolume = 0.5, - this.shakeTriggerCoolDown = const Duration(seconds: 2), - this.shakeTriggerCount = 2, - this.samplingPeriod = const Duration(milliseconds: 100)}) - : _feedback = feedback; - factory _$ShakeDetectionSettingsImpl.fromJson(Map json) => - _$$ShakeDetectionSettingsImplFromJson(json); +class _ShakeDetectionSettings implements ShakeDetectionSettings { + const _ShakeDetectionSettings({this.isEnabled = true, this.direction = ShakeDirection.horizontal, this.threshold = 5, this.shakeAction = ShakeAction.resetSleepTimer, final Set feedback = const {ShakeDetectedFeedback.vibrate}, this.beepVolume = 0.5, this.shakeTriggerCoolDown = const Duration(seconds: 2), this.shakeTriggerCount = 2, this.samplingPeriod = const Duration(milliseconds: 100)}): _feedback = feedback; + factory _ShakeDetectionSettings.fromJson(Map json) => _$ShakeDetectionSettingsFromJson(json); - @override - @JsonKey() - final bool isEnabled; - @override - @JsonKey() - final ShakeDirection direction; - @override - @JsonKey() - final double threshold; - @override - @JsonKey() - final ShakeAction shakeAction; - final Set _feedback; - @override - @JsonKey() - Set get feedback { - if (_feedback is EqualUnmodifiableSetView) return _feedback; - // ignore: implicit_dynamic_type - return EqualUnmodifiableSetView(_feedback); - } - - @override - @JsonKey() - final double beepVolume; - - /// the duration to wait before the shake detection is enabled again - @override - @JsonKey() - final Duration shakeTriggerCoolDown; - - /// the number of shakes required to trigger the action - @override - @JsonKey() - final int shakeTriggerCount; - - /// acceleration sampling interval - @override - @JsonKey() - final Duration samplingPeriod; - - @override - String toString() { - return 'ShakeDetectionSettings(isEnabled: $isEnabled, direction: $direction, threshold: $threshold, shakeAction: $shakeAction, feedback: $feedback, beepVolume: $beepVolume, shakeTriggerCoolDown: $shakeTriggerCoolDown, shakeTriggerCount: $shakeTriggerCount, samplingPeriod: $samplingPeriod)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ShakeDetectionSettingsImpl && - (identical(other.isEnabled, isEnabled) || - other.isEnabled == isEnabled) && - (identical(other.direction, direction) || - other.direction == direction) && - (identical(other.threshold, threshold) || - other.threshold == threshold) && - (identical(other.shakeAction, shakeAction) || - other.shakeAction == shakeAction) && - const DeepCollectionEquality().equals(other._feedback, _feedback) && - (identical(other.beepVolume, beepVolume) || - other.beepVolume == beepVolume) && - (identical(other.shakeTriggerCoolDown, shakeTriggerCoolDown) || - other.shakeTriggerCoolDown == shakeTriggerCoolDown) && - (identical(other.shakeTriggerCount, shakeTriggerCount) || - other.shakeTriggerCount == shakeTriggerCount) && - (identical(other.samplingPeriod, samplingPeriod) || - other.samplingPeriod == samplingPeriod)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - isEnabled, - direction, - threshold, - shakeAction, - const DeepCollectionEquality().hash(_feedback), - beepVolume, - shakeTriggerCoolDown, - shakeTriggerCount, - samplingPeriod); - - /// Create a copy of ShakeDetectionSettings - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ShakeDetectionSettingsImplCopyWith<_$ShakeDetectionSettingsImpl> - get copyWith => __$$ShakeDetectionSettingsImplCopyWithImpl< - _$ShakeDetectionSettingsImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ShakeDetectionSettingsImplToJson( - this, - ); - } +@override@JsonKey() final bool isEnabled; +@override@JsonKey() final ShakeDirection direction; +@override@JsonKey() final double threshold; +@override@JsonKey() final ShakeAction shakeAction; + final Set _feedback; +@override@JsonKey() Set get feedback { + if (_feedback is EqualUnmodifiableSetView) return _feedback; + // ignore: implicit_dynamic_type + return EqualUnmodifiableSetView(_feedback); } -abstract class _ShakeDetectionSettings implements ShakeDetectionSettings { - const factory _ShakeDetectionSettings( - {final bool isEnabled, - final ShakeDirection direction, - final double threshold, - final ShakeAction shakeAction, - final Set feedback, - final double beepVolume, - final Duration shakeTriggerCoolDown, - final int shakeTriggerCount, - final Duration samplingPeriod}) = _$ShakeDetectionSettingsImpl; +@override@JsonKey() final double beepVolume; +/// the duration to wait before the shake detection is enabled again +@override@JsonKey() final Duration shakeTriggerCoolDown; +/// the number of shakes required to trigger the action +@override@JsonKey() final int shakeTriggerCount; +/// acceleration sampling interval +@override@JsonKey() final Duration samplingPeriod; - factory _ShakeDetectionSettings.fromJson(Map json) = - _$ShakeDetectionSettingsImpl.fromJson; +/// Create a copy of ShakeDetectionSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ShakeDetectionSettingsCopyWith<_ShakeDetectionSettings> get copyWith => __$ShakeDetectionSettingsCopyWithImpl<_ShakeDetectionSettings>(this, _$identity); - @override - bool get isEnabled; - @override - ShakeDirection get direction; - @override - double get threshold; - @override - ShakeAction get shakeAction; - @override - Set get feedback; - @override - double get beepVolume; - - /// the duration to wait before the shake detection is enabled again - @override - Duration get shakeTriggerCoolDown; - - /// the number of shakes required to trigger the action - @override - int get shakeTriggerCount; - - /// acceleration sampling interval - @override - Duration get samplingPeriod; - - /// Create a copy of ShakeDetectionSettings - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ShakeDetectionSettingsImplCopyWith<_$ShakeDetectionSettingsImpl> - get copyWith => throw _privateConstructorUsedError; +@override +Map toJson() { + return _$ShakeDetectionSettingsToJson(this, ); } + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ShakeDetectionSettings&&(identical(other.isEnabled, isEnabled) || other.isEnabled == isEnabled)&&(identical(other.direction, direction) || other.direction == direction)&&(identical(other.threshold, threshold) || other.threshold == threshold)&&(identical(other.shakeAction, shakeAction) || other.shakeAction == shakeAction)&&const DeepCollectionEquality().equals(other._feedback, _feedback)&&(identical(other.beepVolume, beepVolume) || other.beepVolume == beepVolume)&&(identical(other.shakeTriggerCoolDown, shakeTriggerCoolDown) || other.shakeTriggerCoolDown == shakeTriggerCoolDown)&&(identical(other.shakeTriggerCount, shakeTriggerCount) || other.shakeTriggerCount == shakeTriggerCount)&&(identical(other.samplingPeriod, samplingPeriod) || other.samplingPeriod == samplingPeriod)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,isEnabled,direction,threshold,shakeAction,const DeepCollectionEquality().hash(_feedback),beepVolume,shakeTriggerCoolDown,shakeTriggerCount,samplingPeriod); + +@override +String toString() { + return 'ShakeDetectionSettings(isEnabled: $isEnabled, direction: $direction, threshold: $threshold, shakeAction: $shakeAction, feedback: $feedback, beepVolume: $beepVolume, shakeTriggerCoolDown: $shakeTriggerCoolDown, shakeTriggerCount: $shakeTriggerCount, samplingPeriod: $samplingPeriod)'; +} + + +} + +/// @nodoc +abstract mixin class _$ShakeDetectionSettingsCopyWith<$Res> implements $ShakeDetectionSettingsCopyWith<$Res> { + factory _$ShakeDetectionSettingsCopyWith(_ShakeDetectionSettings value, $Res Function(_ShakeDetectionSettings) _then) = __$ShakeDetectionSettingsCopyWithImpl; +@override @useResult +$Res call({ + bool isEnabled, ShakeDirection direction, double threshold, ShakeAction shakeAction, Set feedback, double beepVolume, Duration shakeTriggerCoolDown, int shakeTriggerCount, Duration samplingPeriod +}); + + + + +} +/// @nodoc +class __$ShakeDetectionSettingsCopyWithImpl<$Res> + implements _$ShakeDetectionSettingsCopyWith<$Res> { + __$ShakeDetectionSettingsCopyWithImpl(this._self, this._then); + + final _ShakeDetectionSettings _self; + final $Res Function(_ShakeDetectionSettings) _then; + +/// Create a copy of ShakeDetectionSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? isEnabled = null,Object? direction = null,Object? threshold = null,Object? shakeAction = null,Object? feedback = null,Object? beepVolume = null,Object? shakeTriggerCoolDown = null,Object? shakeTriggerCount = null,Object? samplingPeriod = null,}) { + return _then(_ShakeDetectionSettings( +isEnabled: null == isEnabled ? _self.isEnabled : isEnabled // ignore: cast_nullable_to_non_nullable +as bool,direction: null == direction ? _self.direction : direction // ignore: cast_nullable_to_non_nullable +as ShakeDirection,threshold: null == threshold ? _self.threshold : threshold // ignore: cast_nullable_to_non_nullable +as double,shakeAction: null == shakeAction ? _self.shakeAction : shakeAction // ignore: cast_nullable_to_non_nullable +as ShakeAction,feedback: null == feedback ? _self._feedback : feedback // ignore: cast_nullable_to_non_nullable +as Set,beepVolume: null == beepVolume ? _self.beepVolume : beepVolume // ignore: cast_nullable_to_non_nullable +as double,shakeTriggerCoolDown: null == shakeTriggerCoolDown ? _self.shakeTriggerCoolDown : shakeTriggerCoolDown // ignore: cast_nullable_to_non_nullable +as Duration,shakeTriggerCount: null == shakeTriggerCount ? _self.shakeTriggerCount : shakeTriggerCount // ignore: cast_nullable_to_non_nullable +as int,samplingPeriod: null == samplingPeriod ? _self.samplingPeriod : samplingPeriod // ignore: cast_nullable_to_non_nullable +as Duration, + )); +} + + +} + + +/// @nodoc +mixin _$HomePageSettings { + + bool get showPlayButtonOnContinueListeningShelf; bool get showPlayButtonOnContinueSeriesShelf; bool get showPlayButtonOnAllRemainingShelves; bool get showPlayButtonOnListenAgainShelf; +/// Create a copy of HomePageSettings +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$HomePageSettingsCopyWith get copyWith => _$HomePageSettingsCopyWithImpl(this as HomePageSettings, _$identity); + + /// Serializes this HomePageSettings to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is HomePageSettings&&(identical(other.showPlayButtonOnContinueListeningShelf, showPlayButtonOnContinueListeningShelf) || other.showPlayButtonOnContinueListeningShelf == showPlayButtonOnContinueListeningShelf)&&(identical(other.showPlayButtonOnContinueSeriesShelf, showPlayButtonOnContinueSeriesShelf) || other.showPlayButtonOnContinueSeriesShelf == showPlayButtonOnContinueSeriesShelf)&&(identical(other.showPlayButtonOnAllRemainingShelves, showPlayButtonOnAllRemainingShelves) || other.showPlayButtonOnAllRemainingShelves == showPlayButtonOnAllRemainingShelves)&&(identical(other.showPlayButtonOnListenAgainShelf, showPlayButtonOnListenAgainShelf) || other.showPlayButtonOnListenAgainShelf == showPlayButtonOnListenAgainShelf)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,showPlayButtonOnContinueListeningShelf,showPlayButtonOnContinueSeriesShelf,showPlayButtonOnAllRemainingShelves,showPlayButtonOnListenAgainShelf); + +@override +String toString() { + return 'HomePageSettings(showPlayButtonOnContinueListeningShelf: $showPlayButtonOnContinueListeningShelf, showPlayButtonOnContinueSeriesShelf: $showPlayButtonOnContinueSeriesShelf, showPlayButtonOnAllRemainingShelves: $showPlayButtonOnAllRemainingShelves, showPlayButtonOnListenAgainShelf: $showPlayButtonOnListenAgainShelf)'; +} + + +} + +/// @nodoc +abstract mixin class $HomePageSettingsCopyWith<$Res> { + factory $HomePageSettingsCopyWith(HomePageSettings value, $Res Function(HomePageSettings) _then) = _$HomePageSettingsCopyWithImpl; +@useResult +$Res call({ + bool showPlayButtonOnContinueListeningShelf, bool showPlayButtonOnContinueSeriesShelf, bool showPlayButtonOnAllRemainingShelves, bool showPlayButtonOnListenAgainShelf +}); + + + + +} +/// @nodoc +class _$HomePageSettingsCopyWithImpl<$Res> + implements $HomePageSettingsCopyWith<$Res> { + _$HomePageSettingsCopyWithImpl(this._self, this._then); + + final HomePageSettings _self; + final $Res Function(HomePageSettings) _then; + +/// Create a copy of HomePageSettings +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? showPlayButtonOnContinueListeningShelf = null,Object? showPlayButtonOnContinueSeriesShelf = null,Object? showPlayButtonOnAllRemainingShelves = null,Object? showPlayButtonOnListenAgainShelf = null,}) { + return _then(_self.copyWith( +showPlayButtonOnContinueListeningShelf: null == showPlayButtonOnContinueListeningShelf ? _self.showPlayButtonOnContinueListeningShelf : showPlayButtonOnContinueListeningShelf // ignore: cast_nullable_to_non_nullable +as bool,showPlayButtonOnContinueSeriesShelf: null == showPlayButtonOnContinueSeriesShelf ? _self.showPlayButtonOnContinueSeriesShelf : showPlayButtonOnContinueSeriesShelf // ignore: cast_nullable_to_non_nullable +as bool,showPlayButtonOnAllRemainingShelves: null == showPlayButtonOnAllRemainingShelves ? _self.showPlayButtonOnAllRemainingShelves : showPlayButtonOnAllRemainingShelves // ignore: cast_nullable_to_non_nullable +as bool,showPlayButtonOnListenAgainShelf: null == showPlayButtonOnListenAgainShelf ? _self.showPlayButtonOnListenAgainShelf : showPlayButtonOnListenAgainShelf // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + +} + + +/// Adds pattern-matching-related methods to [HomePageSettings]. +extension HomePageSettingsPatterns on HomePageSettings { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _HomePageSettings value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _HomePageSettings() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _HomePageSettings value) $default,){ +final _that = this; +switch (_that) { +case _HomePageSettings(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _HomePageSettings value)? $default,){ +final _that = this; +switch (_that) { +case _HomePageSettings() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool showPlayButtonOnContinueListeningShelf, bool showPlayButtonOnContinueSeriesShelf, bool showPlayButtonOnAllRemainingShelves, bool showPlayButtonOnListenAgainShelf)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _HomePageSettings() when $default != null: +return $default(_that.showPlayButtonOnContinueListeningShelf,_that.showPlayButtonOnContinueSeriesShelf,_that.showPlayButtonOnAllRemainingShelves,_that.showPlayButtonOnListenAgainShelf);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool showPlayButtonOnContinueListeningShelf, bool showPlayButtonOnContinueSeriesShelf, bool showPlayButtonOnAllRemainingShelves, bool showPlayButtonOnListenAgainShelf) $default,) {final _that = this; +switch (_that) { +case _HomePageSettings(): +return $default(_that.showPlayButtonOnContinueListeningShelf,_that.showPlayButtonOnContinueSeriesShelf,_that.showPlayButtonOnAllRemainingShelves,_that.showPlayButtonOnListenAgainShelf);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool showPlayButtonOnContinueListeningShelf, bool showPlayButtonOnContinueSeriesShelf, bool showPlayButtonOnAllRemainingShelves, bool showPlayButtonOnListenAgainShelf)? $default,) {final _that = this; +switch (_that) { +case _HomePageSettings() when $default != null: +return $default(_that.showPlayButtonOnContinueListeningShelf,_that.showPlayButtonOnContinueSeriesShelf,_that.showPlayButtonOnAllRemainingShelves,_that.showPlayButtonOnListenAgainShelf);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _HomePageSettings implements HomePageSettings { + const _HomePageSettings({this.showPlayButtonOnContinueListeningShelf = true, this.showPlayButtonOnContinueSeriesShelf = false, this.showPlayButtonOnAllRemainingShelves = false, this.showPlayButtonOnListenAgainShelf = false}); + factory _HomePageSettings.fromJson(Map json) => _$HomePageSettingsFromJson(json); + +@override@JsonKey() final bool showPlayButtonOnContinueListeningShelf; +@override@JsonKey() final bool showPlayButtonOnContinueSeriesShelf; +@override@JsonKey() final bool showPlayButtonOnAllRemainingShelves; +@override@JsonKey() final bool showPlayButtonOnListenAgainShelf; + +/// Create a copy of HomePageSettings +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$HomePageSettingsCopyWith<_HomePageSettings> get copyWith => __$HomePageSettingsCopyWithImpl<_HomePageSettings>(this, _$identity); + +@override +Map toJson() { + return _$HomePageSettingsToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomePageSettings&&(identical(other.showPlayButtonOnContinueListeningShelf, showPlayButtonOnContinueListeningShelf) || other.showPlayButtonOnContinueListeningShelf == showPlayButtonOnContinueListeningShelf)&&(identical(other.showPlayButtonOnContinueSeriesShelf, showPlayButtonOnContinueSeriesShelf) || other.showPlayButtonOnContinueSeriesShelf == showPlayButtonOnContinueSeriesShelf)&&(identical(other.showPlayButtonOnAllRemainingShelves, showPlayButtonOnAllRemainingShelves) || other.showPlayButtonOnAllRemainingShelves == showPlayButtonOnAllRemainingShelves)&&(identical(other.showPlayButtonOnListenAgainShelf, showPlayButtonOnListenAgainShelf) || other.showPlayButtonOnListenAgainShelf == showPlayButtonOnListenAgainShelf)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,showPlayButtonOnContinueListeningShelf,showPlayButtonOnContinueSeriesShelf,showPlayButtonOnAllRemainingShelves,showPlayButtonOnListenAgainShelf); + +@override +String toString() { + return 'HomePageSettings(showPlayButtonOnContinueListeningShelf: $showPlayButtonOnContinueListeningShelf, showPlayButtonOnContinueSeriesShelf: $showPlayButtonOnContinueSeriesShelf, showPlayButtonOnAllRemainingShelves: $showPlayButtonOnAllRemainingShelves, showPlayButtonOnListenAgainShelf: $showPlayButtonOnListenAgainShelf)'; +} + + +} + +/// @nodoc +abstract mixin class _$HomePageSettingsCopyWith<$Res> implements $HomePageSettingsCopyWith<$Res> { + factory _$HomePageSettingsCopyWith(_HomePageSettings value, $Res Function(_HomePageSettings) _then) = __$HomePageSettingsCopyWithImpl; +@override @useResult +$Res call({ + bool showPlayButtonOnContinueListeningShelf, bool showPlayButtonOnContinueSeriesShelf, bool showPlayButtonOnAllRemainingShelves, bool showPlayButtonOnListenAgainShelf +}); + + + + +} +/// @nodoc +class __$HomePageSettingsCopyWithImpl<$Res> + implements _$HomePageSettingsCopyWith<$Res> { + __$HomePageSettingsCopyWithImpl(this._self, this._then); + + final _HomePageSettings _self; + final $Res Function(_HomePageSettings) _then; + +/// Create a copy of HomePageSettings +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? showPlayButtonOnContinueListeningShelf = null,Object? showPlayButtonOnContinueSeriesShelf = null,Object? showPlayButtonOnAllRemainingShelves = null,Object? showPlayButtonOnListenAgainShelf = null,}) { + return _then(_HomePageSettings( +showPlayButtonOnContinueListeningShelf: null == showPlayButtonOnContinueListeningShelf ? _self.showPlayButtonOnContinueListeningShelf : showPlayButtonOnContinueListeningShelf // ignore: cast_nullable_to_non_nullable +as bool,showPlayButtonOnContinueSeriesShelf: null == showPlayButtonOnContinueSeriesShelf ? _self.showPlayButtonOnContinueSeriesShelf : showPlayButtonOnContinueSeriesShelf // ignore: cast_nullable_to_non_nullable +as bool,showPlayButtonOnAllRemainingShelves: null == showPlayButtonOnAllRemainingShelves ? _self.showPlayButtonOnAllRemainingShelves : showPlayButtonOnAllRemainingShelves // ignore: cast_nullable_to_non_nullable +as bool,showPlayButtonOnListenAgainShelf: null == showPlayButtonOnListenAgainShelf ? _self.showPlayButtonOnListenAgainShelf : showPlayButtonOnListenAgainShelf // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + + +} + +// dart format on diff --git a/lib/settings/models/app_settings.g.dart b/lib/settings/models/app_settings.g.dart index 90b317e..003dac8 100644 --- a/lib/settings/models/app_settings.g.dart +++ b/lib/settings/models/app_settings.g.dart @@ -6,35 +6,41 @@ part of 'app_settings.dart'; // JsonSerializableGenerator // ************************************************************************** -_$AppSettingsImpl _$$AppSettingsImplFromJson(Map json) => - _$AppSettingsImpl( - themeSettings: json['themeSettings'] == null - ? const ThemeSettings() - : ThemeSettings.fromJson( - json['themeSettings'] as Map), - playerSettings: json['playerSettings'] == null - ? const PlayerSettings() - : PlayerSettings.fromJson( - json['playerSettings'] as Map), - sleepTimerSettings: json['sleepTimerSettings'] == null - ? const SleepTimerSettings() - : SleepTimerSettings.fromJson( - json['sleepTimerSettings'] as Map), - downloadSettings: json['downloadSettings'] == null - ? const DownloadSettings() - : DownloadSettings.fromJson( - json['downloadSettings'] as Map), - notificationSettings: json['notificationSettings'] == null - ? const NotificationSettings() - : NotificationSettings.fromJson( - json['notificationSettings'] as Map), - shakeDetectionSettings: json['shakeDetectionSettings'] == null - ? const ShakeDetectionSettings() - : ShakeDetectionSettings.fromJson( - json['shakeDetectionSettings'] as Map), - ); +_AppSettings _$AppSettingsFromJson(Map json) => _AppSettings( + themeSettings: json['themeSettings'] == null + ? const ThemeSettings() + : ThemeSettings.fromJson(json['themeSettings'] as Map), + playerSettings: json['playerSettings'] == null + ? const PlayerSettings() + : PlayerSettings.fromJson(json['playerSettings'] as Map), + sleepTimerSettings: json['sleepTimerSettings'] == null + ? const SleepTimerSettings() + : SleepTimerSettings.fromJson( + json['sleepTimerSettings'] as Map, + ), + downloadSettings: json['downloadSettings'] == null + ? const DownloadSettings() + : DownloadSettings.fromJson( + json['downloadSettings'] as Map, + ), + notificationSettings: json['notificationSettings'] == null + ? const NotificationSettings() + : NotificationSettings.fromJson( + json['notificationSettings'] as Map, + ), + shakeDetectionSettings: json['shakeDetectionSettings'] == null + ? const ShakeDetectionSettings() + : ShakeDetectionSettings.fromJson( + json['shakeDetectionSettings'] as Map, + ), + homePageSettings: json['homePageSettings'] == null + ? const HomePageSettings() + : HomePageSettings.fromJson( + json['homePageSettings'] as Map, + ), +); -Map _$$AppSettingsImplToJson(_$AppSettingsImpl instance) => +Map _$AppSettingsToJson(_AppSettings instance) => { 'themeSettings': instance.themeSettings, 'playerSettings': instance.playerSettings, @@ -42,115 +48,132 @@ Map _$$AppSettingsImplToJson(_$AppSettingsImpl instance) => 'downloadSettings': instance.downloadSettings, 'notificationSettings': instance.notificationSettings, 'shakeDetectionSettings': instance.shakeDetectionSettings, + 'homePageSettings': instance.homePageSettings, }; -_$ThemeSettingsImpl _$$ThemeSettingsImplFromJson(Map json) => - _$ThemeSettingsImpl( - isDarkMode: json['isDarkMode'] as bool? ?? true, +_ThemeSettings _$ThemeSettingsFromJson(Map json) => + _ThemeSettings( + themeMode: + $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ?? + ThemeMode.system, + highContrast: json['highContrast'] as bool? ?? false, + useMaterialThemeFromSystem: + json['useMaterialThemeFromSystem'] as bool? ?? false, + customThemeColor: json['customThemeColor'] as String? ?? '#FF311B92', useMaterialThemeOnItemPage: json['useMaterialThemeOnItemPage'] as bool? ?? true, useCurrentPlayerThemeThroughoutApp: json['useCurrentPlayerThemeThroughoutApp'] as bool? ?? true, ); -Map _$$ThemeSettingsImplToJson(_$ThemeSettingsImpl instance) => +Map _$ThemeSettingsToJson(_ThemeSettings instance) => { - 'isDarkMode': instance.isDarkMode, + 'themeMode': _$ThemeModeEnumMap[instance.themeMode]!, + 'highContrast': instance.highContrast, + 'useMaterialThemeFromSystem': instance.useMaterialThemeFromSystem, + 'customThemeColor': instance.customThemeColor, 'useMaterialThemeOnItemPage': instance.useMaterialThemeOnItemPage, 'useCurrentPlayerThemeThroughoutApp': instance.useCurrentPlayerThemeThroughoutApp, }; -_$PlayerSettingsImpl _$$PlayerSettingsImplFromJson(Map json) => - _$PlayerSettingsImpl( - miniPlayerSettings: json['miniPlayerSettings'] == null - ? const MinimizedPlayerSettings() - : MinimizedPlayerSettings.fromJson( - json['miniPlayerSettings'] as Map), - expandedPlayerSettings: json['expandedPlayerSettings'] == null - ? const ExpandedPlayerSettings() - : ExpandedPlayerSettings.fromJson( - json['expandedPlayerSettings'] as Map), - preferredDefaultVolume: - (json['preferredDefaultVolume'] as num?)?.toDouble() ?? 1, - preferredDefaultSpeed: - (json['preferredDefaultSpeed'] as num?)?.toDouble() ?? 1, - speedOptions: (json['speedOptions'] as List?) - ?.map((e) => (e as num).toDouble()) - .toList() ?? - const [1, 1.25, 1.5, 1.75, 2], - speedIncrement: (json['speedIncrement'] as num?)?.toDouble() ?? 0.05, - minSpeed: (json['minSpeed'] as num?)?.toDouble() ?? 0.1, - maxSpeed: (json['maxSpeed'] as num?)?.toDouble() ?? 4, - minimumPositionForReporting: json['minimumPositionForReporting'] == null - ? const Duration(seconds: 10) - : Duration( - microseconds: - (json['minimumPositionForReporting'] as num).toInt()), - playbackReportInterval: json['playbackReportInterval'] == null - ? const Duration(seconds: 10) - : Duration( - microseconds: (json['playbackReportInterval'] as num).toInt()), - markCompleteWhenTimeLeft: json['markCompleteWhenTimeLeft'] == null - ? const Duration(seconds: 15) - : Duration( - microseconds: (json['markCompleteWhenTimeLeft'] as num).toInt()), - configurePlayerForEveryBook: - json['configurePlayerForEveryBook'] as bool? ?? true, - ); +const _$ThemeModeEnumMap = { + ThemeMode.system: 'system', + ThemeMode.light: 'light', + ThemeMode.dark: 'dark', +}; -Map _$$PlayerSettingsImplToJson( - _$PlayerSettingsImpl instance) => - { - 'miniPlayerSettings': instance.miniPlayerSettings, - 'expandedPlayerSettings': instance.expandedPlayerSettings, - 'preferredDefaultVolume': instance.preferredDefaultVolume, - 'preferredDefaultSpeed': instance.preferredDefaultSpeed, - 'speedOptions': instance.speedOptions, - 'speedIncrement': instance.speedIncrement, - 'minSpeed': instance.minSpeed, - 'maxSpeed': instance.maxSpeed, - 'minimumPositionForReporting': - instance.minimumPositionForReporting.inMicroseconds, - 'playbackReportInterval': instance.playbackReportInterval.inMicroseconds, - 'markCompleteWhenTimeLeft': - instance.markCompleteWhenTimeLeft.inMicroseconds, - 'configurePlayerForEveryBook': instance.configurePlayerForEveryBook, - }; +_PlayerSettings _$PlayerSettingsFromJson( + Map json, +) => _PlayerSettings( + miniPlayerSettings: json['miniPlayerSettings'] == null + ? const MinimizedPlayerSettings() + : MinimizedPlayerSettings.fromJson( + json['miniPlayerSettings'] as Map, + ), + expandedPlayerSettings: json['expandedPlayerSettings'] == null + ? const ExpandedPlayerSettings() + : ExpandedPlayerSettings.fromJson( + json['expandedPlayerSettings'] as Map, + ), + preferredDefaultVolume: + (json['preferredDefaultVolume'] as num?)?.toDouble() ?? 1, + preferredDefaultSpeed: + (json['preferredDefaultSpeed'] as num?)?.toDouble() ?? 1, + speedOptions: + (json['speedOptions'] as List?) + ?.map((e) => (e as num).toDouble()) + .toList() ?? + const [1, 1.25, 1.5, 1.75, 2], + speedIncrement: (json['speedIncrement'] as num?)?.toDouble() ?? 0.05, + minSpeed: (json['minSpeed'] as num?)?.toDouble() ?? 0.1, + maxSpeed: (json['maxSpeed'] as num?)?.toDouble() ?? 4, + minimumPositionForReporting: json['minimumPositionForReporting'] == null + ? const Duration(seconds: 10) + : Duration( + microseconds: (json['minimumPositionForReporting'] as num).toInt(), + ), + playbackReportInterval: json['playbackReportInterval'] == null + ? const Duration(seconds: 10) + : Duration(microseconds: (json['playbackReportInterval'] as num).toInt()), + markCompleteWhenTimeLeft: json['markCompleteWhenTimeLeft'] == null + ? const Duration(seconds: 15) + : Duration( + microseconds: (json['markCompleteWhenTimeLeft'] as num).toInt(), + ), + configurePlayerForEveryBook: + json['configurePlayerForEveryBook'] as bool? ?? true, +); -_$ExpandedPlayerSettingsImpl _$$ExpandedPlayerSettingsImplFromJson( - Map json) => - _$ExpandedPlayerSettingsImpl( - showTotalProgress: json['showTotalProgress'] as bool? ?? false, - showChapterProgress: json['showChapterProgress'] as bool? ?? true, - ); +Map _$PlayerSettingsToJson( + _PlayerSettings instance, +) => { + 'miniPlayerSettings': instance.miniPlayerSettings, + 'expandedPlayerSettings': instance.expandedPlayerSettings, + 'preferredDefaultVolume': instance.preferredDefaultVolume, + 'preferredDefaultSpeed': instance.preferredDefaultSpeed, + 'speedOptions': instance.speedOptions, + 'speedIncrement': instance.speedIncrement, + 'minSpeed': instance.minSpeed, + 'maxSpeed': instance.maxSpeed, + 'minimumPositionForReporting': + instance.minimumPositionForReporting.inMicroseconds, + 'playbackReportInterval': instance.playbackReportInterval.inMicroseconds, + 'markCompleteWhenTimeLeft': instance.markCompleteWhenTimeLeft.inMicroseconds, + 'configurePlayerForEveryBook': instance.configurePlayerForEveryBook, +}; -Map _$$ExpandedPlayerSettingsImplToJson( - _$ExpandedPlayerSettingsImpl instance) => - { - 'showTotalProgress': instance.showTotalProgress, - 'showChapterProgress': instance.showChapterProgress, - }; +_ExpandedPlayerSettings _$ExpandedPlayerSettingsFromJson( + Map json, +) => _ExpandedPlayerSettings( + showTotalProgress: json['showTotalProgress'] as bool? ?? false, + showChapterProgress: json['showChapterProgress'] as bool? ?? true, +); -_$MinimizedPlayerSettingsImpl _$$MinimizedPlayerSettingsImplFromJson( - Map json) => - _$MinimizedPlayerSettingsImpl( - useChapterInfo: json['useChapterInfo'] as bool? ?? false, - ); +Map _$ExpandedPlayerSettingsToJson( + _ExpandedPlayerSettings instance, +) => { + 'showTotalProgress': instance.showTotalProgress, + 'showChapterProgress': instance.showChapterProgress, +}; -Map _$$MinimizedPlayerSettingsImplToJson( - _$MinimizedPlayerSettingsImpl instance) => - { - 'useChapterInfo': instance.useChapterInfo, - }; +_MinimizedPlayerSettings _$MinimizedPlayerSettingsFromJson( + Map json, +) => _MinimizedPlayerSettings( + useChapterInfo: json['useChapterInfo'] as bool? ?? false, +); -_$SleepTimerSettingsImpl _$$SleepTimerSettingsImplFromJson( - Map json) => - _$SleepTimerSettingsImpl( +Map _$MinimizedPlayerSettingsToJson( + _MinimizedPlayerSettings instance, +) => {'useChapterInfo': instance.useChapterInfo}; + +_SleepTimerSettings _$SleepTimerSettingsFromJson(Map json) => + _SleepTimerSettings( defaultDuration: json['defaultDuration'] == null ? const Duration(minutes: 15) : Duration(microseconds: (json['defaultDuration'] as num).toInt()), - presetDurations: (json['presetDurations'] as List?) + presetDurations: + (json['presetDurations'] as List?) ?.map((e) => Duration(microseconds: (e as num).toInt())) .toList() ?? const [ @@ -158,7 +181,7 @@ _$SleepTimerSettingsImpl _$$SleepTimerSettingsImplFromJson( Duration(minutes: 10), Duration(minutes: 15), Duration(minutes: 20), - Duration(minutes: 30) + Duration(minutes: 30), ], maxDuration: json['maxDuration'] == null ? const Duration(minutes: 100) @@ -170,16 +193,18 @@ _$SleepTimerSettingsImpl _$$SleepTimerSettingsImplFromJson( autoRewindWhenStopped: json['autoRewindWhenStopped'] as bool? ?? false, autoRewindDurations: (json['autoRewindDurations'] as Map?)?.map( - (k, e) => MapEntry( - int.parse(k), Duration(microseconds: (e as num).toInt())), - ) ?? - const { - 5: Duration(seconds: 10), - 15: Duration(seconds: 30), - 45: Duration(seconds: 45), - 60: Duration(minutes: 1), - 120: Duration(minutes: 2) - }, + (k, e) => MapEntry( + int.parse(k), + Duration(microseconds: (e as num).toInt()), + ), + ) ?? + const { + 5: Duration(seconds: 10), + 15: Duration(seconds: 30), + 45: Duration(seconds: 45), + 60: Duration(minutes: 1), + 120: Duration(minutes: 2), + }, autoTurnOnTimer: json['autoTurnOnTimer'] as bool? ?? false, alwaysAutoTurnOnTimer: json['alwaysAutoTurnOnTimer'] as bool? ?? false, autoTurnOnTime: json['autoTurnOnTime'] == null @@ -190,27 +215,27 @@ _$SleepTimerSettingsImpl _$$SleepTimerSettingsImplFromJson( : Duration(microseconds: (json['autoTurnOffTime'] as num).toInt()), ); -Map _$$SleepTimerSettingsImplToJson( - _$SleepTimerSettingsImpl instance) => +Map _$SleepTimerSettingsToJson(_SleepTimerSettings instance) => { 'defaultDuration': instance.defaultDuration.inMicroseconds, - 'presetDurations': - instance.presetDurations.map((e) => e.inMicroseconds).toList(), + 'presetDurations': instance.presetDurations + .map((e) => e.inMicroseconds) + .toList(), 'maxDuration': instance.maxDuration.inMicroseconds, 'fadeOutAudio': instance.fadeOutAudio, 'fadeOutDuration': instance.fadeOutDuration.inMicroseconds, 'autoRewindWhenStopped': instance.autoRewindWhenStopped, - 'autoRewindDurations': instance.autoRewindDurations - .map((k, e) => MapEntry(k.toString(), e.inMicroseconds)), + 'autoRewindDurations': instance.autoRewindDurations.map( + (k, e) => MapEntry(k.toString(), e.inMicroseconds), + ), 'autoTurnOnTimer': instance.autoTurnOnTimer, 'alwaysAutoTurnOnTimer': instance.alwaysAutoTurnOnTimer, 'autoTurnOnTime': instance.autoTurnOnTime.inMicroseconds, 'autoTurnOffTime': instance.autoTurnOffTime.inMicroseconds, }; -_$DownloadSettingsImpl _$$DownloadSettingsImplFromJson( - Map json) => - _$DownloadSettingsImpl( +_DownloadSettings _$DownloadSettingsFromJson(Map json) => + _DownloadSettings( requiresWiFi: json['requiresWiFi'] as bool? ?? true, retries: (json['retries'] as num?)?.toInt() ?? 3, allowPause: json['allowPause'] as bool? ?? true, @@ -220,8 +245,7 @@ _$DownloadSettingsImpl _$$DownloadSettingsImplFromJson( (json['maxConcurrentByGroup'] as num?)?.toInt() ?? 3, ); -Map _$$DownloadSettingsImplToJson( - _$DownloadSettingsImpl instance) => +Map _$DownloadSettingsToJson(_DownloadSettings instance) => { 'requiresWiFi': instance.requiresWiFi, 'retries': instance.retries, @@ -231,43 +255,43 @@ Map _$$DownloadSettingsImplToJson( 'maxConcurrentByGroup': instance.maxConcurrentByGroup, }; -_$NotificationSettingsImpl _$$NotificationSettingsImplFromJson( - Map json) => - _$NotificationSettingsImpl( - fastForwardInterval: json['fastForwardInterval'] == null - ? const Duration(seconds: 30) - : Duration( - microseconds: (json['fastForwardInterval'] as num).toInt()), - rewindInterval: json['rewindInterval'] == null - ? const Duration(seconds: 10) - : Duration(microseconds: (json['rewindInterval'] as num).toInt()), - progressBarIsChapterProgress: - json['progressBarIsChapterProgress'] as bool? ?? true, - primaryTitle: json['primaryTitle'] as String? ?? '\$bookTitle', - secondaryTitle: json['secondaryTitle'] as String? ?? '\$author', - mediaControls: (json['mediaControls'] as List?) - ?.map((e) => $enumDecode(_$NotificationMediaControlEnumMap, e)) - .toList() ?? - const [ - NotificationMediaControl.rewind, - NotificationMediaControl.fastForward, - NotificationMediaControl.skipToPreviousChapter, - NotificationMediaControl.skipToNextChapter - ], - ); +_NotificationSettings _$NotificationSettingsFromJson( + Map json, +) => _NotificationSettings( + fastForwardInterval: json['fastForwardInterval'] == null + ? const Duration(seconds: 30) + : Duration(microseconds: (json['fastForwardInterval'] as num).toInt()), + rewindInterval: json['rewindInterval'] == null + ? const Duration(seconds: 10) + : Duration(microseconds: (json['rewindInterval'] as num).toInt()), + progressBarIsChapterProgress: + json['progressBarIsChapterProgress'] as bool? ?? true, + primaryTitle: json['primaryTitle'] as String? ?? '\$bookTitle', + secondaryTitle: json['secondaryTitle'] as String? ?? '\$author', + mediaControls: + (json['mediaControls'] as List?) + ?.map((e) => $enumDecode(_$NotificationMediaControlEnumMap, e)) + .toList() ?? + const [ + NotificationMediaControl.rewind, + NotificationMediaControl.fastForward, + NotificationMediaControl.skipToPreviousChapter, + NotificationMediaControl.skipToNextChapter, + ], +); -Map _$$NotificationSettingsImplToJson( - _$NotificationSettingsImpl instance) => - { - 'fastForwardInterval': instance.fastForwardInterval.inMicroseconds, - 'rewindInterval': instance.rewindInterval.inMicroseconds, - 'progressBarIsChapterProgress': instance.progressBarIsChapterProgress, - 'primaryTitle': instance.primaryTitle, - 'secondaryTitle': instance.secondaryTitle, - 'mediaControls': instance.mediaControls - .map((e) => _$NotificationMediaControlEnumMap[e]!) - .toList(), - }; +Map _$NotificationSettingsToJson( + _NotificationSettings instance, +) => { + 'fastForwardInterval': instance.fastForwardInterval.inMicroseconds, + 'rewindInterval': instance.rewindInterval.inMicroseconds, + 'progressBarIsChapterProgress': instance.progressBarIsChapterProgress, + 'primaryTitle': instance.primaryTitle, + 'secondaryTitle': instance.secondaryTitle, + 'mediaControls': instance.mediaControls + .map((e) => _$NotificationMediaControlEnumMap[e]!) + .toList(), +}; const _$NotificationMediaControlEnumMap = { NotificationMediaControl.fastForward: 'fastForward', @@ -278,47 +302,47 @@ const _$NotificationMediaControlEnumMap = { NotificationMediaControl.skipToPreviousChapter: 'skipToPreviousChapter', }; -_$ShakeDetectionSettingsImpl _$$ShakeDetectionSettingsImplFromJson( - Map json) => - _$ShakeDetectionSettingsImpl( - isEnabled: json['isEnabled'] as bool? ?? true, - direction: - $enumDecodeNullable(_$ShakeDirectionEnumMap, json['direction']) ?? - ShakeDirection.horizontal, - threshold: (json['threshold'] as num?)?.toDouble() ?? 5, - shakeAction: - $enumDecodeNullable(_$ShakeActionEnumMap, json['shakeAction']) ?? - ShakeAction.resetSleepTimer, - feedback: (json['feedback'] as List?) - ?.map((e) => $enumDecode(_$ShakeDetectedFeedbackEnumMap, e)) - .toSet() ?? - const {ShakeDetectedFeedback.vibrate}, - beepVolume: (json['beepVolume'] as num?)?.toDouble() ?? 0.5, - shakeTriggerCoolDown: json['shakeTriggerCoolDown'] == null - ? const Duration(seconds: 2) - : Duration( - microseconds: (json['shakeTriggerCoolDown'] as num).toInt()), - shakeTriggerCount: (json['shakeTriggerCount'] as num?)?.toInt() ?? 2, - samplingPeriod: json['samplingPeriod'] == null - ? const Duration(milliseconds: 100) - : Duration(microseconds: (json['samplingPeriod'] as num).toInt()), - ); +_ShakeDetectionSettings _$ShakeDetectionSettingsFromJson( + Map json, +) => _ShakeDetectionSettings( + isEnabled: json['isEnabled'] as bool? ?? true, + direction: + $enumDecodeNullable(_$ShakeDirectionEnumMap, json['direction']) ?? + ShakeDirection.horizontal, + threshold: (json['threshold'] as num?)?.toDouble() ?? 5, + shakeAction: + $enumDecodeNullable(_$ShakeActionEnumMap, json['shakeAction']) ?? + ShakeAction.resetSleepTimer, + feedback: + (json['feedback'] as List?) + ?.map((e) => $enumDecode(_$ShakeDetectedFeedbackEnumMap, e)) + .toSet() ?? + const {ShakeDetectedFeedback.vibrate}, + beepVolume: (json['beepVolume'] as num?)?.toDouble() ?? 0.5, + shakeTriggerCoolDown: json['shakeTriggerCoolDown'] == null + ? const Duration(seconds: 2) + : Duration(microseconds: (json['shakeTriggerCoolDown'] as num).toInt()), + shakeTriggerCount: (json['shakeTriggerCount'] as num?)?.toInt() ?? 2, + samplingPeriod: json['samplingPeriod'] == null + ? const Duration(milliseconds: 100) + : Duration(microseconds: (json['samplingPeriod'] as num).toInt()), +); -Map _$$ShakeDetectionSettingsImplToJson( - _$ShakeDetectionSettingsImpl instance) => - { - 'isEnabled': instance.isEnabled, - 'direction': _$ShakeDirectionEnumMap[instance.direction]!, - 'threshold': instance.threshold, - 'shakeAction': _$ShakeActionEnumMap[instance.shakeAction]!, - 'feedback': instance.feedback - .map((e) => _$ShakeDetectedFeedbackEnumMap[e]!) - .toList(), - 'beepVolume': instance.beepVolume, - 'shakeTriggerCoolDown': instance.shakeTriggerCoolDown.inMicroseconds, - 'shakeTriggerCount': instance.shakeTriggerCount, - 'samplingPeriod': instance.samplingPeriod.inMicroseconds, - }; +Map _$ShakeDetectionSettingsToJson( + _ShakeDetectionSettings instance, +) => { + 'isEnabled': instance.isEnabled, + 'direction': _$ShakeDirectionEnumMap[instance.direction]!, + 'threshold': instance.threshold, + 'shakeAction': _$ShakeActionEnumMap[instance.shakeAction]!, + 'feedback': instance.feedback + .map((e) => _$ShakeDetectedFeedbackEnumMap[e]!) + .toList(), + 'beepVolume': instance.beepVolume, + 'shakeTriggerCoolDown': instance.shakeTriggerCoolDown.inMicroseconds, + 'shakeTriggerCount': instance.shakeTriggerCount, + 'samplingPeriod': instance.samplingPeriod.inMicroseconds, +}; const _$ShakeDirectionEnumMap = { ShakeDirection.horizontal: 'horizontal', @@ -337,3 +361,27 @@ const _$ShakeDetectedFeedbackEnumMap = { ShakeDetectedFeedback.vibrate: 'vibrate', ShakeDetectedFeedback.beep: 'beep', }; + +_HomePageSettings _$HomePageSettingsFromJson(Map json) => + _HomePageSettings( + showPlayButtonOnContinueListeningShelf: + json['showPlayButtonOnContinueListeningShelf'] as bool? ?? true, + showPlayButtonOnContinueSeriesShelf: + json['showPlayButtonOnContinueSeriesShelf'] as bool? ?? false, + showPlayButtonOnAllRemainingShelves: + json['showPlayButtonOnAllRemainingShelves'] as bool? ?? false, + showPlayButtonOnListenAgainShelf: + json['showPlayButtonOnListenAgainShelf'] as bool? ?? false, + ); + +Map _$HomePageSettingsToJson( + _HomePageSettings instance, +) => { + 'showPlayButtonOnContinueListeningShelf': + instance.showPlayButtonOnContinueListeningShelf, + 'showPlayButtonOnContinueSeriesShelf': + instance.showPlayButtonOnContinueSeriesShelf, + 'showPlayButtonOnAllRemainingShelves': + instance.showPlayButtonOnAllRemainingShelves, + 'showPlayButtonOnListenAgainShelf': instance.showPlayButtonOnListenAgainShelf, +}; diff --git a/lib/settings/models/audiobookshelf_server.dart b/lib/settings/models/audiobookshelf_server.dart index 0c97e41..cbc14a2 100644 --- a/lib/settings/models/audiobookshelf_server.dart +++ b/lib/settings/models/audiobookshelf_server.dart @@ -7,7 +7,7 @@ typedef AudiobookShelfUri = Uri; /// Represents a audiobookshelf server @freezed -class AudiobookShelfServer with _$AudiobookShelfServer { +sealed class AudiobookShelfServer with _$AudiobookShelfServer { const factory AudiobookShelfServer({ required AudiobookShelfUri serverUrl, // String? serverName, diff --git a/lib/settings/models/audiobookshelf_server.freezed.dart b/lib/settings/models/audiobookshelf_server.freezed.dart index 39f095a..26ec5c4 100644 --- a/lib/settings/models/audiobookshelf_server.freezed.dart +++ b/lib/settings/models/audiobookshelf_server.freezed.dart @@ -1,5 +1,5 @@ -// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark @@ -9,161 +9,263 @@ part of 'audiobookshelf_server.dart'; // FreezedGenerator // ************************************************************************** +// dart format off T _$identity(T value) => value; -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -AudiobookShelfServer _$AudiobookShelfServerFromJson(Map json) { - return _AudiobookShelfServer.fromJson(json); -} - /// @nodoc mixin _$AudiobookShelfServer { - Uri get serverUrl => throw _privateConstructorUsedError; + + AudiobookShelfUri get serverUrl; +/// Create a copy of AudiobookShelfServer +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$AudiobookShelfServerCopyWith get copyWith => _$AudiobookShelfServerCopyWithImpl(this as AudiobookShelfServer, _$identity); /// Serializes this AudiobookShelfServer to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is AudiobookShelfServer&&(identical(other.serverUrl, serverUrl) || other.serverUrl == serverUrl)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,serverUrl); + +@override +String toString() { + return 'AudiobookShelfServer(serverUrl: $serverUrl)'; +} + - /// Create a copy of AudiobookShelfServer - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $AudiobookShelfServerCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $AudiobookShelfServerCopyWith<$Res> { - factory $AudiobookShelfServerCopyWith(AudiobookShelfServer value, - $Res Function(AudiobookShelfServer) then) = - _$AudiobookShelfServerCopyWithImpl<$Res, AudiobookShelfServer>; - @useResult - $Res call({Uri serverUrl}); -} +abstract mixin class $AudiobookShelfServerCopyWith<$Res> { + factory $AudiobookShelfServerCopyWith(AudiobookShelfServer value, $Res Function(AudiobookShelfServer) _then) = _$AudiobookShelfServerCopyWithImpl; +@useResult +$Res call({ + AudiobookShelfUri serverUrl +}); + + + +} /// @nodoc -class _$AudiobookShelfServerCopyWithImpl<$Res, - $Val extends AudiobookShelfServer> +class _$AudiobookShelfServerCopyWithImpl<$Res> implements $AudiobookShelfServerCopyWith<$Res> { - _$AudiobookShelfServerCopyWithImpl(this._value, this._then); + _$AudiobookShelfServerCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final AudiobookShelfServer _self; + final $Res Function(AudiobookShelfServer) _then; - /// Create a copy of AudiobookShelfServer - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? serverUrl = null, - }) { - return _then(_value.copyWith( - serverUrl: null == serverUrl - ? _value.serverUrl - : serverUrl // ignore: cast_nullable_to_non_nullable - as Uri, - ) as $Val); - } +/// Create a copy of AudiobookShelfServer +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? serverUrl = null,}) { + return _then(_self.copyWith( +serverUrl: null == serverUrl ? _self.serverUrl : serverUrl // ignore: cast_nullable_to_non_nullable +as AudiobookShelfUri, + )); } -/// @nodoc -abstract class _$$AudiobookShelfServerImplCopyWith<$Res> - implements $AudiobookShelfServerCopyWith<$Res> { - factory _$$AudiobookShelfServerImplCopyWith(_$AudiobookShelfServerImpl value, - $Res Function(_$AudiobookShelfServerImpl) then) = - __$$AudiobookShelfServerImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({Uri serverUrl}); } -/// @nodoc -class __$$AudiobookShelfServerImplCopyWithImpl<$Res> - extends _$AudiobookShelfServerCopyWithImpl<$Res, _$AudiobookShelfServerImpl> - implements _$$AudiobookShelfServerImplCopyWith<$Res> { - __$$AudiobookShelfServerImplCopyWithImpl(_$AudiobookShelfServerImpl _value, - $Res Function(_$AudiobookShelfServerImpl) _then) - : super(_value, _then); - /// Create a copy of AudiobookShelfServer - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? serverUrl = null, - }) { - return _then(_$AudiobookShelfServerImpl( - serverUrl: null == serverUrl - ? _value.serverUrl - : serverUrl // ignore: cast_nullable_to_non_nullable - as Uri, - )); - } +/// Adds pattern-matching-related methods to [AudiobookShelfServer]. +extension AudiobookShelfServerPatterns on AudiobookShelfServer { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _AudiobookShelfServer value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _AudiobookShelfServer() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _AudiobookShelfServer value) $default,){ +final _that = this; +switch (_that) { +case _AudiobookShelfServer(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _AudiobookShelfServer value)? $default,){ +final _that = this; +switch (_that) { +case _AudiobookShelfServer() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( AudiobookShelfUri serverUrl)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _AudiobookShelfServer() when $default != null: +return $default(_that.serverUrl);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( AudiobookShelfUri serverUrl) $default,) {final _that = this; +switch (_that) { +case _AudiobookShelfServer(): +return $default(_that.serverUrl);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( AudiobookShelfUri serverUrl)? $default,) {final _that = this; +switch (_that) { +case _AudiobookShelfServer() when $default != null: +return $default(_that.serverUrl);case _: + return null; + +} +} + } /// @nodoc @JsonSerializable() -class _$AudiobookShelfServerImpl implements _AudiobookShelfServer { - const _$AudiobookShelfServerImpl({required this.serverUrl}); - factory _$AudiobookShelfServerImpl.fromJson(Map json) => - _$$AudiobookShelfServerImplFromJson(json); +class _AudiobookShelfServer implements AudiobookShelfServer { + const _AudiobookShelfServer({required this.serverUrl}); + factory _AudiobookShelfServer.fromJson(Map json) => _$AudiobookShelfServerFromJson(json); - @override - final Uri serverUrl; +@override final AudiobookShelfUri serverUrl; - @override - String toString() { - return 'AudiobookShelfServer(serverUrl: $serverUrl)'; - } +/// Create a copy of AudiobookShelfServer +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$AudiobookShelfServerCopyWith<_AudiobookShelfServer> get copyWith => __$AudiobookShelfServerCopyWithImpl<_AudiobookShelfServer>(this, _$identity); - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$AudiobookShelfServerImpl && - (identical(other.serverUrl, serverUrl) || - other.serverUrl == serverUrl)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, serverUrl); - - /// Create a copy of AudiobookShelfServer - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$AudiobookShelfServerImplCopyWith<_$AudiobookShelfServerImpl> - get copyWith => - __$$AudiobookShelfServerImplCopyWithImpl<_$AudiobookShelfServerImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$AudiobookShelfServerImplToJson( - this, - ); - } +@override +Map toJson() { + return _$AudiobookShelfServerToJson(this, ); } -abstract class _AudiobookShelfServer implements AudiobookShelfServer { - const factory _AudiobookShelfServer({required final Uri serverUrl}) = - _$AudiobookShelfServerImpl; - - factory _AudiobookShelfServer.fromJson(Map json) = - _$AudiobookShelfServerImpl.fromJson; - - @override - Uri get serverUrl; - - /// Create a copy of AudiobookShelfServer - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$AudiobookShelfServerImplCopyWith<_$AudiobookShelfServerImpl> - get copyWith => throw _privateConstructorUsedError; +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AudiobookShelfServer&&(identical(other.serverUrl, serverUrl) || other.serverUrl == serverUrl)); } + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,serverUrl); + +@override +String toString() { + return 'AudiobookShelfServer(serverUrl: $serverUrl)'; +} + + +} + +/// @nodoc +abstract mixin class _$AudiobookShelfServerCopyWith<$Res> implements $AudiobookShelfServerCopyWith<$Res> { + factory _$AudiobookShelfServerCopyWith(_AudiobookShelfServer value, $Res Function(_AudiobookShelfServer) _then) = __$AudiobookShelfServerCopyWithImpl; +@override @useResult +$Res call({ + AudiobookShelfUri serverUrl +}); + + + + +} +/// @nodoc +class __$AudiobookShelfServerCopyWithImpl<$Res> + implements _$AudiobookShelfServerCopyWith<$Res> { + __$AudiobookShelfServerCopyWithImpl(this._self, this._then); + + final _AudiobookShelfServer _self; + final $Res Function(_AudiobookShelfServer) _then; + +/// Create a copy of AudiobookShelfServer +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? serverUrl = null,}) { + return _then(_AudiobookShelfServer( +serverUrl: null == serverUrl ? _self.serverUrl : serverUrl // ignore: cast_nullable_to_non_nullable +as AudiobookShelfUri, + )); +} + + +} + +// dart format on diff --git a/lib/settings/models/audiobookshelf_server.g.dart b/lib/settings/models/audiobookshelf_server.g.dart index a876683..e8504ec 100644 --- a/lib/settings/models/audiobookshelf_server.g.dart +++ b/lib/settings/models/audiobookshelf_server.g.dart @@ -6,14 +6,10 @@ part of 'audiobookshelf_server.dart'; // JsonSerializableGenerator // ************************************************************************** -_$AudiobookShelfServerImpl _$$AudiobookShelfServerImplFromJson( - Map json) => - _$AudiobookShelfServerImpl( - serverUrl: Uri.parse(json['serverUrl'] as String), - ); +_AudiobookShelfServer _$AudiobookShelfServerFromJson( + Map json, +) => _AudiobookShelfServer(serverUrl: Uri.parse(json['serverUrl'] as String)); -Map _$$AudiobookShelfServerImplToJson( - _$AudiobookShelfServerImpl instance) => - { - 'serverUrl': instance.serverUrl.toString(), - }; +Map _$AudiobookShelfServerToJson( + _AudiobookShelfServer instance, +) => {'serverUrl': instance.serverUrl.toString()}; diff --git a/lib/settings/models/authenticated_user.dart b/lib/settings/models/authenticated_user.dart index 04f2bd0..97613ab 100644 --- a/lib/settings/models/authenticated_user.dart +++ b/lib/settings/models/authenticated_user.dart @@ -6,13 +6,12 @@ part 'authenticated_user.g.dart'; /// authenticated user with server and credentials @freezed -class AuthenticatedUser with _$AuthenticatedUser { +sealed class AuthenticatedUser with _$AuthenticatedUser { const factory AuthenticatedUser({ required AudiobookShelfServer server, required String authToken, - String? id, + required String id, String? username, - String? password, }) = _AuthenticatedUser; factory AuthenticatedUser.fromJson(Map json) => diff --git a/lib/settings/models/authenticated_user.freezed.dart b/lib/settings/models/authenticated_user.freezed.dart index e582928..3010150 100644 --- a/lib/settings/models/authenticated_user.freezed.dart +++ b/lib/settings/models/authenticated_user.freezed.dart @@ -1,5 +1,5 @@ -// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark @@ -9,260 +9,290 @@ part of 'authenticated_user.dart'; // FreezedGenerator // ************************************************************************** +// dart format off T _$identity(T value) => value; -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -AuthenticatedUser _$AuthenticatedUserFromJson(Map json) { - return _AuthenticatedUser.fromJson(json); -} - /// @nodoc mixin _$AuthenticatedUser { - AudiobookShelfServer get server => throw _privateConstructorUsedError; - String get authToken => throw _privateConstructorUsedError; - String? get id => throw _privateConstructorUsedError; - String? get username => throw _privateConstructorUsedError; - String? get password => throw _privateConstructorUsedError; + + AudiobookShelfServer get server; String get authToken; String get id; String? get username; +/// Create a copy of AuthenticatedUser +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$AuthenticatedUserCopyWith get copyWith => _$AuthenticatedUserCopyWithImpl(this as AuthenticatedUser, _$identity); /// Serializes this AuthenticatedUser to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is AuthenticatedUser&&(identical(other.server, server) || other.server == server)&&(identical(other.authToken, authToken) || other.authToken == authToken)&&(identical(other.id, id) || other.id == id)&&(identical(other.username, username) || other.username == username)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,server,authToken,id,username); + +@override +String toString() { + return 'AuthenticatedUser(server: $server, authToken: $authToken, id: $id, username: $username)'; +} + - /// Create a copy of AuthenticatedUser - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $AuthenticatedUserCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class $AuthenticatedUserCopyWith<$Res> { - factory $AuthenticatedUserCopyWith( - AuthenticatedUser value, $Res Function(AuthenticatedUser) then) = - _$AuthenticatedUserCopyWithImpl<$Res, AuthenticatedUser>; - @useResult - $Res call( - {AudiobookShelfServer server, - String authToken, - String? id, - String? username, - String? password}); +abstract mixin class $AuthenticatedUserCopyWith<$Res> { + factory $AuthenticatedUserCopyWith(AuthenticatedUser value, $Res Function(AuthenticatedUser) _then) = _$AuthenticatedUserCopyWithImpl; +@useResult +$Res call({ + AudiobookShelfServer server, String authToken, String id, String? username +}); + + +$AudiobookShelfServerCopyWith<$Res> get server; - $AudiobookShelfServerCopyWith<$Res> get server; } - /// @nodoc -class _$AuthenticatedUserCopyWithImpl<$Res, $Val extends AuthenticatedUser> +class _$AuthenticatedUserCopyWithImpl<$Res> implements $AuthenticatedUserCopyWith<$Res> { - _$AuthenticatedUserCopyWithImpl(this._value, this._then); + _$AuthenticatedUserCopyWithImpl(this._self, this._then); - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; + final AuthenticatedUser _self; + final $Res Function(AuthenticatedUser) _then; - /// Create a copy of AuthenticatedUser - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? server = null, - Object? authToken = null, - Object? id = freezed, - Object? username = freezed, - Object? password = freezed, - }) { - return _then(_value.copyWith( - server: null == server - ? _value.server - : server // ignore: cast_nullable_to_non_nullable - as AudiobookShelfServer, - authToken: null == authToken - ? _value.authToken - : authToken // ignore: cast_nullable_to_non_nullable - as String, - id: freezed == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as String?, - username: freezed == username - ? _value.username - : username // ignore: cast_nullable_to_non_nullable - as String?, - password: freezed == password - ? _value.password - : password // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); - } - - /// Create a copy of AuthenticatedUser - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $AudiobookShelfServerCopyWith<$Res> get server { - return $AudiobookShelfServerCopyWith<$Res>(_value.server, (value) { - return _then(_value.copyWith(server: value) as $Val); - }); - } +/// Create a copy of AuthenticatedUser +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? server = null,Object? authToken = null,Object? id = null,Object? username = freezed,}) { + return _then(_self.copyWith( +server: null == server ? _self.server : server // ignore: cast_nullable_to_non_nullable +as AudiobookShelfServer,authToken: null == authToken ? _self.authToken : authToken // ignore: cast_nullable_to_non_nullable +as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,username: freezed == username ? _self.username : username // ignore: cast_nullable_to_non_nullable +as String?, + )); +} +/// Create a copy of AuthenticatedUser +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$AudiobookShelfServerCopyWith<$Res> get server { + + return $AudiobookShelfServerCopyWith<$Res>(_self.server, (value) { + return _then(_self.copyWith(server: value)); + }); +} } -/// @nodoc -abstract class _$$AuthenticatedUserImplCopyWith<$Res> - implements $AuthenticatedUserCopyWith<$Res> { - factory _$$AuthenticatedUserImplCopyWith(_$AuthenticatedUserImpl value, - $Res Function(_$AuthenticatedUserImpl) then) = - __$$AuthenticatedUserImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {AudiobookShelfServer server, - String authToken, - String? id, - String? username, - String? password}); - @override - $AudiobookShelfServerCopyWith<$Res> get server; +/// Adds pattern-matching-related methods to [AuthenticatedUser]. +extension AuthenticatedUserPatterns on AuthenticatedUser { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _AuthenticatedUser value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _AuthenticatedUser() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _AuthenticatedUser value) $default,){ +final _that = this; +switch (_that) { +case _AuthenticatedUser(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _AuthenticatedUser value)? $default,){ +final _that = this; +switch (_that) { +case _AuthenticatedUser() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( AudiobookShelfServer server, String authToken, String id, String? username)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _AuthenticatedUser() when $default != null: +return $default(_that.server,_that.authToken,_that.id,_that.username);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( AudiobookShelfServer server, String authToken, String id, String? username) $default,) {final _that = this; +switch (_that) { +case _AuthenticatedUser(): +return $default(_that.server,_that.authToken,_that.id,_that.username);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( AudiobookShelfServer server, String authToken, String id, String? username)? $default,) {final _that = this; +switch (_that) { +case _AuthenticatedUser() when $default != null: +return $default(_that.server,_that.authToken,_that.id,_that.username);case _: + return null; + +} } -/// @nodoc -class __$$AuthenticatedUserImplCopyWithImpl<$Res> - extends _$AuthenticatedUserCopyWithImpl<$Res, _$AuthenticatedUserImpl> - implements _$$AuthenticatedUserImplCopyWith<$Res> { - __$$AuthenticatedUserImplCopyWithImpl(_$AuthenticatedUserImpl _value, - $Res Function(_$AuthenticatedUserImpl) _then) - : super(_value, _then); - - /// Create a copy of AuthenticatedUser - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? server = null, - Object? authToken = null, - Object? id = freezed, - Object? username = freezed, - Object? password = freezed, - }) { - return _then(_$AuthenticatedUserImpl( - server: null == server - ? _value.server - : server // ignore: cast_nullable_to_non_nullable - as AudiobookShelfServer, - authToken: null == authToken - ? _value.authToken - : authToken // ignore: cast_nullable_to_non_nullable - as String, - id: freezed == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as String?, - username: freezed == username - ? _value.username - : username // ignore: cast_nullable_to_non_nullable - as String?, - password: freezed == password - ? _value.password - : password // ignore: cast_nullable_to_non_nullable - as String?, - )); - } } /// @nodoc @JsonSerializable() -class _$AuthenticatedUserImpl implements _AuthenticatedUser { - const _$AuthenticatedUserImpl( - {required this.server, - required this.authToken, - this.id, - this.username, - this.password}); - factory _$AuthenticatedUserImpl.fromJson(Map json) => - _$$AuthenticatedUserImplFromJson(json); +class _AuthenticatedUser implements AuthenticatedUser { + const _AuthenticatedUser({required this.server, required this.authToken, required this.id, this.username}); + factory _AuthenticatedUser.fromJson(Map json) => _$AuthenticatedUserFromJson(json); - @override - final AudiobookShelfServer server; - @override - final String authToken; - @override - final String? id; - @override - final String? username; - @override - final String? password; +@override final AudiobookShelfServer server; +@override final String authToken; +@override final String id; +@override final String? username; - @override - String toString() { - return 'AuthenticatedUser(server: $server, authToken: $authToken, id: $id, username: $username, password: $password)'; - } +/// Create a copy of AuthenticatedUser +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$AuthenticatedUserCopyWith<_AuthenticatedUser> get copyWith => __$AuthenticatedUserCopyWithImpl<_AuthenticatedUser>(this, _$identity); - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$AuthenticatedUserImpl && - (identical(other.server, server) || other.server == server) && - (identical(other.authToken, authToken) || - other.authToken == authToken) && - (identical(other.id, id) || other.id == id) && - (identical(other.username, username) || - other.username == username) && - (identical(other.password, password) || - other.password == password)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => - Object.hash(runtimeType, server, authToken, id, username, password); - - /// Create a copy of AuthenticatedUser - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$AuthenticatedUserImplCopyWith<_$AuthenticatedUserImpl> get copyWith => - __$$AuthenticatedUserImplCopyWithImpl<_$AuthenticatedUserImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$AuthenticatedUserImplToJson( - this, - ); - } +@override +Map toJson() { + return _$AuthenticatedUserToJson(this, ); } -abstract class _AuthenticatedUser implements AuthenticatedUser { - const factory _AuthenticatedUser( - {required final AudiobookShelfServer server, - required final String authToken, - final String? id, - final String? username, - final String? password}) = _$AuthenticatedUserImpl; - - factory _AuthenticatedUser.fromJson(Map json) = - _$AuthenticatedUserImpl.fromJson; - - @override - AudiobookShelfServer get server; - @override - String get authToken; - @override - String? get id; - @override - String? get username; - @override - String? get password; - - /// Create a copy of AuthenticatedUser - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$AuthenticatedUserImplCopyWith<_$AuthenticatedUserImpl> get copyWith => - throw _privateConstructorUsedError; +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AuthenticatedUser&&(identical(other.server, server) || other.server == server)&&(identical(other.authToken, authToken) || other.authToken == authToken)&&(identical(other.id, id) || other.id == id)&&(identical(other.username, username) || other.username == username)); } + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,server,authToken,id,username); + +@override +String toString() { + return 'AuthenticatedUser(server: $server, authToken: $authToken, id: $id, username: $username)'; +} + + +} + +/// @nodoc +abstract mixin class _$AuthenticatedUserCopyWith<$Res> implements $AuthenticatedUserCopyWith<$Res> { + factory _$AuthenticatedUserCopyWith(_AuthenticatedUser value, $Res Function(_AuthenticatedUser) _then) = __$AuthenticatedUserCopyWithImpl; +@override @useResult +$Res call({ + AudiobookShelfServer server, String authToken, String id, String? username +}); + + +@override $AudiobookShelfServerCopyWith<$Res> get server; + +} +/// @nodoc +class __$AuthenticatedUserCopyWithImpl<$Res> + implements _$AuthenticatedUserCopyWith<$Res> { + __$AuthenticatedUserCopyWithImpl(this._self, this._then); + + final _AuthenticatedUser _self; + final $Res Function(_AuthenticatedUser) _then; + +/// Create a copy of AuthenticatedUser +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? server = null,Object? authToken = null,Object? id = null,Object? username = freezed,}) { + return _then(_AuthenticatedUser( +server: null == server ? _self.server : server // ignore: cast_nullable_to_non_nullable +as AudiobookShelfServer,authToken: null == authToken ? _self.authToken : authToken // ignore: cast_nullable_to_non_nullable +as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,username: freezed == username ? _self.username : username // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +/// Create a copy of AuthenticatedUser +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$AudiobookShelfServerCopyWith<$Res> get server { + + return $AudiobookShelfServerCopyWith<$Res>(_self.server, (value) { + return _then(_self.copyWith(server: value)); + }); +} +} + +// dart format on diff --git a/lib/settings/models/authenticated_user.g.dart b/lib/settings/models/authenticated_user.g.dart index 0752807..926c7d8 100644 --- a/lib/settings/models/authenticated_user.g.dart +++ b/lib/settings/models/authenticated_user.g.dart @@ -6,23 +6,20 @@ part of 'authenticated_user.dart'; // JsonSerializableGenerator // ************************************************************************** -_$AuthenticatedUserImpl _$$AuthenticatedUserImplFromJson( - Map json) => - _$AuthenticatedUserImpl( - server: - AudiobookShelfServer.fromJson(json['server'] as Map), +_AuthenticatedUser _$AuthenticatedUserFromJson(Map json) => + _AuthenticatedUser( + server: AudiobookShelfServer.fromJson( + json['server'] as Map, + ), authToken: json['authToken'] as String, - id: json['id'] as String?, + id: json['id'] as String, username: json['username'] as String?, - password: json['password'] as String?, ); -Map _$$AuthenticatedUserImplToJson( - _$AuthenticatedUserImpl instance) => +Map _$AuthenticatedUserToJson(_AuthenticatedUser instance) => { 'server': instance.server, 'authToken': instance.authToken, 'id': instance.id, 'username': instance.username, - 'password': instance.password, }; diff --git a/lib/settings/view/app_settings_page.dart b/lib/settings/view/app_settings_page.dart index 77afaae..a193752 100644 --- a/lib/settings/view/app_settings_page.dart +++ b/lib/settings/view/app_settings_page.dart @@ -7,85 +7,40 @@ import 'package:flutter_settings_ui/flutter_settings_ui.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:material_symbols_icons/symbols.dart'; -import 'package:vaani/api/authenticated_user_provider.dart'; -import 'package:vaani/api/server_provider.dart'; import 'package:vaani/router/router.dart'; import 'package:vaani/settings/app_settings_provider.dart'; import 'package:vaani/settings/models/app_settings.dart' as model; +import 'package:vaani/settings/view/buttons.dart'; import 'package:vaani/settings/view/simple_settings_page.dart'; import 'package:vaani/settings/view/widgets/navigation_with_switch_tile.dart'; class AppSettingsPage extends HookConsumerWidget { - const AppSettingsPage({ - super.key, - }); + const AppSettingsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final appSettings = ref.watch(appSettingsProvider); - final registeredServers = ref.watch(audiobookShelfServerProvider); - final registeredServersAsList = registeredServers.toList(); - final availableUsers = ref.watch(authenticatedUserProvider); - final serverURIController = useTextEditingController(); final sleepTimerSettings = appSettings.sleepTimerSettings; return SimpleSettingsPage( title: const Text('App Settings'), sections: [ - // Appearance section - SettingsSection( - margin: const EdgeInsetsDirectional.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - title: Text( - 'Appearance', - style: Theme.of(context).textTheme.titleLarge, - ), - tiles: [ - SettingsTile.switchTile( - initialValue: appSettings.themeSettings.isDarkMode, - title: const Text('Dark Mode'), - description: const Text('we all know dark mode is better'), - leading: appSettings.themeSettings.isDarkMode - ? const Icon(Icons.dark_mode) - : const Icon(Icons.light_mode), - onToggle: (value) { - ref.read(appSettingsProvider.notifier).toggleDarkMode(); - }, - ), - SettingsTile.switchTile( - initialValue: - appSettings.themeSettings.useMaterialThemeOnItemPage, - title: const Text('Adaptive Theme on Item Page'), - description: const Text( - 'get fancy with the colors on the item page at the cost of some performance', - ), - leading: appSettings.themeSettings.useMaterialThemeOnItemPage - ? const Icon(Icons.auto_fix_high) - : const Icon(Icons.auto_fix_off), - onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( - appSettings.copyWith.themeSettings( - useMaterialThemeOnItemPage: value, - ), - ); - }, - ), - ], - ), - // General section SettingsSection( margin: const EdgeInsetsDirectional.symmetric( horizontal: 16.0, vertical: 8.0, ), - title: Text( - 'General', - style: Theme.of(context).textTheme.titleLarge, - ), + title: Text('General', style: Theme.of(context).textTheme.titleLarge), tiles: [ + SettingsTile( + title: const Text('Player Settings'), + leading: const Icon(Icons.play_arrow), + description: const Text('Customize the player settings'), + onPressed: (context) { + context.pushNamed(Routes.playerSettings.name); + }, + ), NavigationWithSwitchTile( title: const Text('Auto Turn On Sleep Timer'), description: const Text( @@ -99,13 +54,55 @@ class AppSettingsPage extends HookConsumerWidget { }, value: sleepTimerSettings.autoTurnOnTimer, onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.sleepTimerSettings( autoTurnOnTimer: value, ), ); }, ), + NavigationWithSwitchTile( + title: const Text('Shake Detector'), + leading: const Icon(Icons.vibration), + description: const Text('Customize the shake detector settings'), + value: appSettings.shakeDetectionSettings.isEnabled, + onPressed: (context) { + context.pushNamed(Routes.shakeDetectorSettings.name); + }, + onToggle: (value) { + ref + .read(appSettingsProvider.notifier) + .update( + appSettings.copyWith.shakeDetectionSettings( + isEnabled: value, + ), + ); + }, + ), + ], + ), + + // Appearance section + SettingsSection( + margin: const EdgeInsetsDirectional.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + title: Text( + 'Appearance', + style: Theme.of(context).textTheme.titleLarge, + ), + tiles: [ + SettingsTile.navigation( + leading: const Icon(Icons.color_lens), + title: const Text('Theme Settings'), + description: const Text('Customize the app theme'), + onPressed: (context) { + context.pushNamed(Routes.themeSettings.name); + }, + ), SettingsTile( title: const Text('Notification Media Player'), leading: const Icon(Icons.play_lesson), @@ -116,36 +113,17 @@ class AppSettingsPage extends HookConsumerWidget { context.pushNamed(Routes.notificationSettings.name); }, ), - SettingsTile( - title: const Text('Player Settings'), - leading: const Icon(Icons.play_arrow), - description: const Text( - 'Customize the player settings', - ), + SettingsTile.navigation( + leading: const Icon(Icons.home_filled), + title: const Text('Home Page Settings'), + description: const Text('Customize the home page'), onPressed: (context) { - context.pushNamed(Routes.playerSettings.name); - }, - ), - NavigationWithSwitchTile( - title: const Text('Shake Detector'), - leading: const Icon(Icons.vibration), - description: const Text( - 'Customize the shake detector settings', - ), - value: appSettings.shakeDetectionSettings.isEnabled, - onPressed: (context) { - context.pushNamed(Routes.shakeDetectorSettings.name); - }, - onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( - appSettings.copyWith.shakeDetectionSettings( - isEnabled: value, - ), - ); + context.pushNamed(Routes.homePageSettings.name); }, ), ], ), + // Backup and Restore section SettingsSection( margin: const EdgeInsetsDirectional.symmetric( @@ -160,21 +138,15 @@ class AppSettingsPage extends HookConsumerWidget { SettingsTile( title: const Text('Copy to Clipboard'), leading: const Icon(Icons.copy), - description: const Text( - 'Copy the app settings to the clipboard', - ), + description: const Text('Copy the app settings to the clipboard'), onPressed: (context) async { // copy to clipboard await Clipboard.setData( - ClipboardData( - text: jsonEncode(appSettings.toJson()), - ), + ClipboardData(text: jsonEncode(appSettings.toJson())), ); // show toast ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Settings copied to clipboard'), - ), + const SnackBar(content: Text('Settings copied to clipboard')), ); }, ), @@ -185,61 +157,11 @@ class AppSettingsPage extends HookConsumerWidget { 'Restore the app settings from the backup', ), onPressed: (context) { - final formKey = GlobalKey(); // show a dialog to get the backup showDialog( context: context, builder: (context) { - return AlertDialog( - title: const Text('Restore Backup'), - content: Form( - key: formKey, - child: TextFormField( - controller: serverURIController, - decoration: const InputDecoration( - labelText: 'Backup', - hintText: 'Paste the backup here', - ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please paste the backup here'; - } - return null; - }, - ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - if (formKey.currentState!.validate()) { - final backup = serverURIController.text; - final newSettings = model.AppSettings.fromJson( - // decode the backup as json - jsonDecode(backup), - ); - ref - .read(appSettingsProvider.notifier) - .update(newSettings); - Navigator.of(context).pop(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Settings restored'), - ), - ); - // clear the backup - serverURIController.clear(); - } - }, - child: const Text('Restore'), - ), - ], - ); + return RestoreDialogue(); }, ); }, @@ -292,3 +214,73 @@ class AppSettingsPage extends HookConsumerWidget { ); } } + +class RestoreDialogue extends HookConsumerWidget { + const RestoreDialogue({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final formKey = useMemoized(() => GlobalKey()); + final settings = useState(null); + + final settingsInputController = useTextEditingController(); + return AlertDialog( + title: const Text('Restore Backup'), + content: Form( + key: formKey, + child: TextFormField( + autofocus: true, + decoration: InputDecoration( + labelText: 'Backup', + hintText: 'Paste the backup here', + // clear button + suffixIcon: IconButton( + icon: Icon(Icons.clear), + onPressed: () { + settingsInputController.clear(); + }, + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please paste the backup here'; + } + try { + // try to decode the backup + settings.value = model.AppSettings.fromJson(jsonDecode(value)); + } catch (e) { + return 'Invalid backup'; + } + return null; + }, + ), + ), + actions: [ + CancelButton(), + TextButton( + onPressed: () { + if (formKey.currentState!.validate()) { + if (settings.value == null) { + ScaffoldMessenger.of( + context, + ).showSnackBar(const SnackBar(content: Text('Invalid backup'))); + return; + } + ref.read(appSettingsProvider.notifier).update(settings.value!); + settingsInputController.clear(); + Navigator.of(context).pop(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Settings restored')), + ); + } else { + ScaffoldMessenger.of( + context, + ).showSnackBar(const SnackBar(content: Text('Invalid backup'))); + } + }, + child: const Text('Restore'), + ), + ], + ); + } +} diff --git a/lib/settings/view/auto_sleep_timer_settings_page.dart b/lib/settings/view/auto_sleep_timer_settings_page.dart index fe7d4b3..58fcf8e 100644 --- a/lib/settings/view/auto_sleep_timer_settings_page.dart +++ b/lib/settings/view/auto_sleep_timer_settings_page.dart @@ -7,16 +7,15 @@ import 'package:vaani/settings/view/simple_settings_page.dart'; import 'package:vaani/shared/extensions/time_of_day.dart'; class AutoSleepTimerSettingsPage extends HookConsumerWidget { - const AutoSleepTimerSettingsPage({ - super.key, - }); + const AutoSleepTimerSettingsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final appSettings = ref.watch(appSettingsProvider); final sleepTimerSettings = appSettings.sleepTimerSettings; - var enabled = sleepTimerSettings.autoTurnOnTimer && + var enabled = + sleepTimerSettings.autoTurnOnTimer && !sleepTimerSettings.alwaysAutoTurnOnTimer; final selectedValueColor = enabled ? Theme.of(context).colorScheme.primary @@ -40,7 +39,9 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget { ? const Icon(Symbols.time_auto) : const Icon(Symbols.timer_off), onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.sleepTimerSettings( autoTurnOnTimer: value, ), @@ -63,7 +64,9 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget { initialTime: sleepTimerSettings.autoTurnOnTime.toTimeOfDay(), ); if (selected != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.sleepTimerSettings( autoTurnOnTime: selected.toDuration(), ), @@ -89,7 +92,9 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget { initialTime: sleepTimerSettings.autoTurnOffTime.toTimeOfDay(), ); if (selected != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.sleepTimerSettings( autoTurnOffTime: selected.toDuration(), ), @@ -97,9 +102,9 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget { } }, trailing: Text( - sleepTimerSettings.autoTurnOffTime - .toTimeOfDay() - .format(context), + sleepTimerSettings.autoTurnOffTime.toTimeOfDay().format( + context, + ), style: TextStyle(color: selectedValueColor), ), ), @@ -112,7 +117,9 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget { 'Always turn on the sleep timer, no matter what', ), onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.sleepTimerSettings( alwaysAutoTurnOnTimer: value, ), diff --git a/lib/settings/view/buttons.dart b/lib/settings/view/buttons.dart index 0dea11f..209dc21 100644 --- a/lib/settings/view/buttons.dart +++ b/lib/settings/view/buttons.dart @@ -1,31 +1,26 @@ import 'package:flutter/material.dart'; class OkButton extends StatelessWidget { - const OkButton({ - super.key, - this.onPressed, - }); + const OkButton({super.key, this.onPressed}); + + final void Function()? onPressed; + + @override + Widget build(BuildContext context) { + return TextButton(onPressed: onPressed, child: const Text('OK')); + } +} + +class CancelButton extends StatelessWidget { + const CancelButton({super.key, this.onPressed}); final void Function()? onPressed; - @override - Widget build(BuildContext context) { - return TextButton( - onPressed: onPressed, - child: const Text('OK'), - ); - } -} - -class CancelButton extends StatelessWidget { - const CancelButton({ - super.key, - }); - @override Widget build(BuildContext context) { return TextButton( onPressed: () { + onPressed?.call(); Navigator.of(context).pop(); }, child: const Text('Cancel'), diff --git a/lib/settings/view/home_page_settings_page.dart b/lib/settings/view/home_page_settings_page.dart new file mode 100644 index 0000000..9b3b3fb --- /dev/null +++ b/lib/settings/view/home_page_settings_page.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:flutter_settings_ui/flutter_settings_ui.dart'; +import 'package:vaani/settings/app_settings_provider.dart'; +import 'package:vaani/settings/view/simple_settings_page.dart' + show SimpleSettingsPage; + +class HomePageSettingsPage extends HookConsumerWidget { + const HomePageSettingsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final appSettings = ref.watch(appSettingsProvider); + final appSettingsNotifier = ref.read(appSettingsProvider.notifier); + + return SimpleSettingsPage( + title: Text('Home Page Settings'), + sections: [ + SettingsSection( + title: const Text('Quick Play'), + margin: const EdgeInsetsDirectional.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + tiles: [ + SettingsTile.switchTile( + initialValue: appSettings + .homePageSettings + .showPlayButtonOnContinueListeningShelf, + title: const Text('Continue Listening'), + leading: const Icon(Icons.play_arrow), + description: const Text( + 'Show play button for books in currently listening shelf', + ), + onToggle: (value) { + appSettingsNotifier.update( + appSettings.copyWith( + homePageSettings: appSettings.homePageSettings.copyWith( + showPlayButtonOnContinueListeningShelf: value, + ), + ), + ); + }, + ), + SettingsTile.switchTile( + title: const Text('Continue Series'), + leading: const Icon(Icons.play_arrow), + description: const Text( + 'Show play button for books in continue series shelf', + ), + initialValue: appSettings + .homePageSettings + .showPlayButtonOnContinueSeriesShelf, + onToggle: (value) { + appSettingsNotifier.update( + appSettings.copyWith( + homePageSettings: appSettings.homePageSettings.copyWith( + showPlayButtonOnContinueSeriesShelf: value, + ), + ), + ); + }, + ), + SettingsTile.switchTile( + title: const Text('Other shelves'), + leading: const Icon(Icons.all_inclusive), + description: const Text( + 'Show play button for all books in all remaining shelves', + ), + initialValue: appSettings + .homePageSettings + .showPlayButtonOnAllRemainingShelves, + onToggle: (value) { + appSettingsNotifier.update( + appSettings.copyWith( + homePageSettings: appSettings.homePageSettings.copyWith( + showPlayButtonOnAllRemainingShelves: value, + ), + ), + ); + }, + ), + SettingsTile.switchTile( + title: const Text('Listen Again'), + leading: const Icon(Icons.replay), + description: const Text( + 'Show play button for all books in listen again shelf', + ), + initialValue: + appSettings.homePageSettings.showPlayButtonOnListenAgainShelf, + onToggle: (value) { + appSettingsNotifier.update( + appSettings.copyWith( + homePageSettings: appSettings.homePageSettings.copyWith( + showPlayButtonOnListenAgainShelf: value, + ), + ), + ); + }, + ), + ], + ), + ], + ); + } +} diff --git a/lib/settings/view/notification_settings_page.dart b/lib/settings/view/notification_settings_page.dart index 140d21c..c306f87 100644 --- a/lib/settings/view/notification_settings_page.dart +++ b/lib/settings/view/notification_settings_page.dart @@ -9,9 +9,7 @@ import 'package:vaani/settings/view/simple_settings_page.dart'; import 'package:vaani/shared/extensions/enum.dart'; class NotificationSettingsPage extends HookConsumerWidget { - const NotificationSettingsPage({ - super.key, - }); + const NotificationSettingsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -59,7 +57,9 @@ class NotificationSettingsPage extends HookConsumerWidget { }, ); if (selectedTitle != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.notificationSettings( primaryTitle: selectedTitle, ), @@ -97,7 +97,9 @@ class NotificationSettingsPage extends HookConsumerWidget { }, ); if (selectedTitle != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.notificationSettings( secondaryTitle: selectedTitle, ), @@ -118,7 +120,9 @@ class NotificationSettingsPage extends HookConsumerWidget { child: TimeIntervalSlider( defaultValue: notificationSettings.fastForwardInterval, onChangedEnd: (interval) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.notificationSettings( fastForwardInterval: interval, ), @@ -141,7 +145,9 @@ class NotificationSettingsPage extends HookConsumerWidget { child: TimeIntervalSlider( defaultValue: notificationSettings.rewindInterval, onChangedEnd: (interval) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.notificationSettings( rewindInterval: interval, ), @@ -162,26 +168,23 @@ class NotificationSettingsPage extends HookConsumerWidget { trailing: Wrap( spacing: 8.0, children: notificationSettings.mediaControls - .map( - (control) => Icon( - control.icon, - color: primaryColor, - ), - ) + .map((control) => Icon(control.icon, color: primaryColor)) .toList(), ), onPressed: (context) async { final selectedControls = await showDialog>( - context: context, - builder: (context) { - return MediaControlsPicker( - selectedControls: notificationSettings.mediaControls, + context: context, + builder: (context) { + return MediaControlsPicker( + selectedControls: notificationSettings.mediaControls, + ); + }, ); - }, - ); if (selectedControls != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.notificationSettings( mediaControls: selectedControls, ), @@ -194,11 +197,14 @@ class NotificationSettingsPage extends HookConsumerWidget { SettingsTile.switchTile( title: const Text('Show Chapter Progress'), leading: const Icon(Icons.book), - description: - const Text('instead of the overall progress of the book'), + description: const Text( + 'instead of the overall progress of the book', + ), initialValue: notificationSettings.progressBarIsChapterProgress, onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.notificationSettings( progressBarIsChapterProgress: value, ), @@ -213,10 +219,7 @@ class NotificationSettingsPage extends HookConsumerWidget { } class MediaControlsPicker extends HookConsumerWidget { - const MediaControlsPicker({ - super.key, - required this.selectedControls, - }); + const MediaControlsPicker({super.key, required this.selectedControls}); final List selectedControls; diff --git a/lib/settings/view/player_settings_page.dart b/lib/settings/view/player_settings_page.dart index 58b050f..1d93e71 100644 --- a/lib/settings/view/player_settings_page.dart +++ b/lib/settings/view/player_settings_page.dart @@ -9,9 +9,7 @@ import 'package:vaani/settings/view/simple_settings_page.dart'; import 'package:vaani/shared/extensions/duration_format.dart'; class PlayerSettingsPage extends HookConsumerWidget { - const PlayerSettingsPage({ - super.key, - }); + const PlayerSettingsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -37,7 +35,9 @@ class PlayerSettingsPage extends HookConsumerWidget { ), initialValue: playerSettings.configurePlayerForEveryBook, onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.playerSettings( configurePlayerForEveryBook: value, ), @@ -50,8 +50,10 @@ class PlayerSettingsPage extends HookConsumerWidget { title: const Text('Default Speed'), trailing: Text( '${playerSettings.preferredDefaultSpeed}x', - style: - TextStyle(color: primaryColor, fontWeight: FontWeight.bold), + style: TextStyle( + color: primaryColor, + fontWeight: FontWeight.bold, + ), ), leading: const Icon(Icons.speed), onPressed: (context) async { @@ -62,7 +64,9 @@ class PlayerSettingsPage extends HookConsumerWidget { ), ); if (newSpeed != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.playerSettings( preferredDefaultSpeed: newSpeed, ), @@ -75,8 +79,10 @@ class PlayerSettingsPage extends HookConsumerWidget { title: const Text('Speed Options'), description: Text( playerSettings.speedOptions.map((e) => '${e}x').join(', '), - style: - TextStyle(fontWeight: FontWeight.bold, color: primaryColor), + style: TextStyle( + fontWeight: FontWeight.bold, + color: primaryColor, + ), ), leading: const Icon(Icons.speed), onPressed: (context) async { @@ -87,7 +93,9 @@ class PlayerSettingsPage extends HookConsumerWidget { ), ); if (newSpeedOptions != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.playerSettings( speedOptions: newSpeedOptions..sort(), ), @@ -110,7 +118,8 @@ class PlayerSettingsPage extends HookConsumerWidget { children: [ TextSpan( text: playerSettings - .minimumPositionForReporting.smartBinaryFormat, + .minimumPositionForReporting + .smartBinaryFormat, style: TextStyle( fontWeight: FontWeight.bold, color: primaryColor, @@ -133,7 +142,9 @@ class PlayerSettingsPage extends HookConsumerWidget { }, ); if (newDuration != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.playerSettings( minimumPositionForReporting: newDuration, ), @@ -150,7 +161,8 @@ class PlayerSettingsPage extends HookConsumerWidget { children: [ TextSpan( text: playerSettings - .markCompleteWhenTimeLeft.smartBinaryFormat, + .markCompleteWhenTimeLeft + .smartBinaryFormat, style: TextStyle( fontWeight: FontWeight.bold, color: primaryColor, @@ -173,7 +185,9 @@ class PlayerSettingsPage extends HookConsumerWidget { }, ); if (newDuration != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.playerSettings( markCompleteWhenTimeLeft: newDuration, ), @@ -190,7 +204,8 @@ class PlayerSettingsPage extends HookConsumerWidget { children: [ TextSpan( text: playerSettings - .playbackReportInterval.smartBinaryFormat, + .playbackReportInterval + .smartBinaryFormat, style: TextStyle( fontWeight: FontWeight.bold, color: primaryColor, @@ -213,7 +228,9 @@ class PlayerSettingsPage extends HookConsumerWidget { }, ); if (newDuration != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.playerSettings( playbackReportInterval: newDuration, ), @@ -237,7 +254,9 @@ class PlayerSettingsPage extends HookConsumerWidget { initialValue: playerSettings.expandedPlayerSettings.showTotalProgress, onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.playerSettings .expandedPlayerSettings(showTotalProgress: value), ); @@ -253,7 +272,9 @@ class PlayerSettingsPage extends HookConsumerWidget { initialValue: playerSettings.expandedPlayerSettings.showChapterProgress, onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.playerSettings( expandedPlayerSettings: playerSettings .expandedPlayerSettings @@ -306,17 +327,15 @@ class TimeDurationSelector extends HookConsumerWidget { } class SpeedPicker extends HookConsumerWidget { - const SpeedPicker({ - super.key, - this.initialValue = 1, - }); + const SpeedPicker({super.key, this.initialValue = 1}); final double initialValue; @override Widget build(BuildContext context, WidgetRef ref) { - final speedController = - useTextEditingController(text: initialValue.toString()); + final speedController = useTextEditingController( + text: initialValue.toString(), + ); final speed = useState(initialValue); return AlertDialog( title: const Text('Select Speed'), @@ -368,30 +387,32 @@ class SpeedOptionsPicker extends HookConsumerWidget { Wrap( spacing: 8.0, runSpacing: 8.0, - children: speedOptions.value - .map( - (speed) => Chip( - label: Text('${speed}x'), - onDeleted: speed == 1 - ? null - : () { - speedOptions.value = - speedOptions.value.where((element) { - // speed option 1 can't be removed - return element != speed; - }).toList(); - }, - ), - ) - .toList() - ..sort((a, b) { - // if (a.label == const Text('1x')) { - // return -1; - // } else if (b.label == const Text('1x')) { - // return 1; - // } - return a.label.toString().compareTo(b.label.toString()); - }), + children: + speedOptions.value + .map( + (speed) => Chip( + label: Text('${speed}x'), + onDeleted: speed == 1 + ? null + : () { + speedOptions.value = speedOptions.value.where(( + element, + ) { + // speed option 1 can't be removed + return element != speed; + }).toList(); + }, + ), + ) + .toList() + ..sort((a, b) { + // if (a.label == const Text('1x')) { + // return -1; + // } else if (b.label == const Text('1x')) { + // return 1; + // } + return a.label.toString().compareTo(b.label.toString()); + }), ), TextField( focusNode: focusNode, diff --git a/lib/settings/view/shake_detector_settings_page.dart b/lib/settings/view/shake_detector_settings_page.dart index 98c5b8d..fe39b53 100644 --- a/lib/settings/view/shake_detector_settings_page.dart +++ b/lib/settings/view/shake_detector_settings_page.dart @@ -9,9 +9,7 @@ import 'package:vaani/settings/view/simple_settings_page.dart'; import 'package:vaani/shared/extensions/enum.dart'; class ShakeDetectorSettingsPage extends HookConsumerWidget { - const ShakeDetectorSettingsPage({ - super.key, - }); + const ShakeDetectorSettingsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -41,7 +39,9 @@ class ShakeDetectorSettingsPage extends HookConsumerWidget { ), initialValue: shakeDetectionSettings.isEnabled, onToggle: (value) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.shakeDetectionSettings( isEnabled: value, ), @@ -77,7 +77,9 @@ class ShakeDetectorSettingsPage extends HookConsumerWidget { ); if (newThreshold != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.shakeDetectionSettings( threshold: newThreshold, ), @@ -107,7 +109,9 @@ class ShakeDetectorSettingsPage extends HookConsumerWidget { ); if (newShakeAction != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.shakeDetectionSettings( shakeAction: newShakeAction, ), @@ -131,26 +135,23 @@ class ShakeDetectorSettingsPage extends HookConsumerWidget { ) : Wrap( spacing: 8.0, - children: shakeDetectionSettings.feedback.map( - (feedback) { - return Icon( - feedback.icon, - color: selectedValueColor, - ); - }, - ).toList(), + children: shakeDetectionSettings.feedback.map((feedback) { + return Icon(feedback.icon, color: selectedValueColor); + }).toList(), ), onPressed: (context) async { final newFeedback = await showDialog>( - context: context, - builder: (context) => ShakeFeedbackSelector( - initialValue: shakeDetectionSettings.feedback, - ), - ); + context: context, + builder: (context) => ShakeFeedbackSelector( + initialValue: shakeDetectionSettings.feedback, + ), + ); if (newFeedback != null) { - ref.read(appSettingsProvider.notifier).update( + ref + .read(appSettingsProvider.notifier) + .update( appSettings.copyWith.shakeDetectionSettings( feedback: newFeedback, ), @@ -256,10 +257,7 @@ class ShakeActionSelector extends HookConsumerWidget { } class ShakeForceSelector extends HookConsumerWidget { - const ShakeForceSelector({ - super.key, - this.initialValue = 6, - }); + const ShakeForceSelector({super.key, this.initialValue = 6}); final double initialValue; @@ -291,9 +289,7 @@ class ShakeForceSelector extends HookConsumerWidget { shakeForce.value = 0; }, ), - helper: const Text( - 'Enter a number to set the threshold in m/s²', - ), + helper: const Text('Enter a number to set the threshold in m/s²'), ), ), Wrap( diff --git a/lib/settings/view/simple_settings_page.dart b/lib/settings/view/simple_settings_page.dart index 471b022..e1f9fd7 100644 --- a/lib/settings/view/simple_settings_page.dart +++ b/lib/settings/view/simple_settings_page.dart @@ -1,13 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_settings_ui/flutter_settings_ui.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:vaani/features/player/view/mini_player_bottom_padding.dart'; class SimpleSettingsPage extends HookConsumerWidget { - const SimpleSettingsPage({ - super.key, - this.title, - this.sections, - }); + const SimpleSettingsPage({super.key, this.title, this.sections}); final Widget? title; final List? sections; @@ -33,21 +30,20 @@ class SimpleSettingsPage extends HookConsumerWidget { ), if (sections != null) SliverList( - delegate: SliverChildListDelegate( - [ - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(20)), - child: SettingsList( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - sections: sections!, - ), + delegate: SliverChildListDelegate([ + ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(20)), + child: SettingsList( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + sections: sections!, ), - ], - ), + ), + ]), ), // some padding at the bottom const SliverPadding(padding: EdgeInsets.only(bottom: 20)), + SliverToBoxAdapter(child: MiniPlayerBottomPadding()), ], ), ); diff --git a/lib/settings/view/theme_settings_page.dart b/lib/settings/view/theme_settings_page.dart new file mode 100644 index 0000000..8727be7 --- /dev/null +++ b/lib/settings/view/theme_settings_page.dart @@ -0,0 +1,215 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_settings_ui/flutter_settings_ui.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:vaani/settings/app_settings_provider.dart'; +import 'package:vaani/settings/view/buttons.dart'; +import 'package:vaani/settings/view/simple_settings_page.dart'; +import 'package:vaani/shared/extensions/enum.dart'; + +class ThemeSettingsPage extends HookConsumerWidget { + const ThemeSettingsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final appSettings = ref.watch(appSettingsProvider); + final themeSettings = appSettings.themeSettings; + final primaryColor = Theme.of(context).colorScheme.primary; + + return SimpleSettingsPage( + title: const Text('Theme Settings'), + sections: [ + SettingsSection( + margin: const EdgeInsetsDirectional.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + tiles: [ + // choose system , light or dark theme + SettingsTile( + title: const Text('Theme Mode'), + description: SegmentedButton( + expandedInsets: const EdgeInsets.only(top: 8.0), + showSelectedIcon: true, + selectedIcon: const Icon(Icons.check), + selected: {themeSettings.themeMode}, + onSelectionChanged: (newSelection) { + ref + .read(appSettingsProvider.notifier) + .update( + appSettings.copyWith.themeSettings( + themeMode: newSelection.first, + ), + ); + }, + segments: [ + ButtonSegment( + value: ThemeMode.light, + icon: Icon(Icons.light_mode), + label: const Text('Light'), + ), + ButtonSegment( + value: ThemeMode.system, + icon: Icon(Icons.auto_awesome), + label: const Text('System'), + ), + ButtonSegment( + value: ThemeMode.dark, + icon: Icon(Icons.dark_mode), + label: const Text('Dark'), + ), + ], + ), + leading: Icon( + themeSettings.themeMode == ThemeMode.light + ? Icons.light_mode + : themeSettings.themeMode == ThemeMode.dark + ? Icons.dark_mode + : Icons.auto_awesome, + ), + ), + + // high contrast mode + SettingsTile.switchTile( + leading: themeSettings.highContrast + ? const Icon(Icons.accessibility) + : const Icon(Icons.accessibility_new_outlined), + initialValue: themeSettings.highContrast, + title: const Text('High Contrast Mode'), + description: const Text( + 'Increase the contrast between the background and the text', + ), + onToggle: (value) { + ref + .read(appSettingsProvider.notifier) + .update( + appSettings.copyWith.themeSettings(highContrast: value), + ); + }, + ), + + // use material theme from system + SettingsTile.switchTile( + initialValue: themeSettings.useMaterialThemeFromSystem, + title: Platform.isAndroid + ? const Text('Use Material You') + : const Text('Material Theme from System'), + description: const Text( + 'Use the system theme colors for the app', + ), + leading: themeSettings.useMaterialThemeFromSystem + ? const Icon(Icons.auto_awesome) + : const Icon(Icons.auto_fix_off), + onToggle: (value) { + ref + .read(appSettingsProvider.notifier) + .update( + appSettings.copyWith.themeSettings( + useMaterialThemeFromSystem: value, + ), + ); + }, + ), + + // TODO choose the primary color + // SettingsTile.navigation( + // title: const Text('Primary Color'), + // description: const Text( + // 'Choose the primary color for the app', + // ), + // leading: const Icon(Icons.colorize), + // trailing: Icon( + // Icons.circle, + // color: themeSettings.customThemeColor.toColor(), + // ), + // onPressed: (context) async { + // final selectedColor = await showDialog( + // context: context, + // builder: (context) { + // return SimpleDialog( + // title: const Text('Select Primary Color'), + // children: [ + // for (final color in Colors.primaries) + // SimpleDialogOption( + // onPressed: () { + // Navigator.pop(context, color); + // }, + // child: Container( + // color: color, + // height: 48, + // ), + // ), + // ], + // ); + // }, + // ); + // if (selectedColor != null) { + // ref.read(appSettingsProvider.notifier).update( + // appSettings.copyWith.themeSettings( + // customThemeColor: selectedColor.toHexString(), + // ), + // ); + // } + // }, + // ), + + // use theme throughout the app when playing item + SettingsTile.switchTile( + initialValue: themeSettings.useCurrentPlayerThemeThroughoutApp, + title: const Text('Adapt theme from currently playing item'), + description: const Text( + 'Use the theme colors from the currently playing item for the app', + ), + leading: themeSettings.useCurrentPlayerThemeThroughoutApp + ? const Icon(Icons.auto_fix_high) + : const Icon(Icons.auto_fix_off), + onToggle: (value) { + ref + .read(appSettingsProvider.notifier) + .update( + appSettings.copyWith.themeSettings( + useCurrentPlayerThemeThroughoutApp: value, + ), + ); + }, + ), + + SettingsTile.switchTile( + initialValue: themeSettings.useMaterialThemeOnItemPage, + title: const Text('Adaptive Theme on Item Page'), + description: const Text( + 'get fancy with the colors on the item page at the cost of some performance', + ), + leading: themeSettings.useMaterialThemeOnItemPage + ? const Icon(Icons.auto_fix_high) + : const Icon(Icons.auto_fix_off), + onToggle: (value) { + ref + .read(appSettingsProvider.notifier) + .update( + appSettings.copyWith.themeSettings( + useMaterialThemeOnItemPage: value, + ), + ); + }, + ), + ], + ), + ], + ); + } +} + +extension ColorExtension on Color { + String toHexString() { + return '#${value.toRadixString(16).substring(2)}'; + } +} + +extension StringExtension on String { + Color toColor() { + return Color(int.parse('0xff$substring(1)')); + } +} diff --git a/lib/settings/view/widgets/navigation_with_switch_tile.dart b/lib/settings/view/widgets/navigation_with_switch_tile.dart index dbc8ecf..f2d90f9 100644 --- a/lib/settings/view/widgets/navigation_with_switch_tile.dart +++ b/lib/settings/view/widgets/navigation_with_switch_tile.dart @@ -40,14 +40,11 @@ class NavigationWithSwitchTile extends AbstractSettingsTile { child: Row( children: [ VerticalDivider( - color: Theme.of(context).dividerColor.withOpacity(0.5), + color: Theme.of(context).dividerColor.withValues(alpha: 0.5), indent: 8.0, endIndent: 8.0, ), - Switch.adaptive( - value: value, - onChanged: onToggle, - ), + Switch.adaptive(value: value, onChanged: onToggle), ], ), ), diff --git a/lib/shared/extensions/enum.dart b/lib/shared/extensions/enum.dart index e0e6c09..0def3ea 100644 --- a/lib/shared/extensions/enum.dart +++ b/lib/shared/extensions/enum.dart @@ -13,10 +13,7 @@ extension TitleCase on Enum { String get pascalCase { // capitalize the first letter of each word return name - .replaceAllMapped( - RegExp(r'([A-Z])'), - (match) => ' ${match.group(0)}', - ) + .replaceAllMapped(RegExp(r'([A-Z])'), (match) => ' ${match.group(0)}') .trim() .split(' ') .map((word) => word[0].toUpperCase() + word.substring(1)) diff --git a/lib/shared/extensions/model_conversions.dart b/lib/shared/extensions/model_conversions.dart index 38403d8..b3ff0aa 100644 --- a/lib/shared/extensions/model_conversions.dart +++ b/lib/shared/extensions/model_conversions.dart @@ -47,8 +47,8 @@ extension ShelfConversion on Shelf { extension UserConversion on User { UserWithSessionAndMostRecentProgress - get asUserWithSessionAndMostRecentProgress => - UserWithSessionAndMostRecentProgress.fromJson(toJson()); + get asUserWithSessionAndMostRecentProgress => + UserWithSessionAndMostRecentProgress.fromJson(toJson()); User get asUser => User.fromJson(toJson()); } diff --git a/lib/shared/extensions/obfuscation.dart b/lib/shared/extensions/obfuscation.dart new file mode 100644 index 0000000..2a057a4 --- /dev/null +++ b/lib/shared/extensions/obfuscation.dart @@ -0,0 +1,162 @@ +import 'package:flutter/foundation.dart'; +import 'package:http/http.dart' as http; +import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk; +import 'package:vaani/settings/models/api_settings.dart'; +import 'package:vaani/settings/models/audiobookshelf_server.dart'; +import 'package:vaani/settings/models/authenticated_user.dart'; + +// bool kReleaseMode = true; + +extension ObfuscateString on String { + String obfuscate() { + if (!kReleaseMode) { + return this; + } + return 'obfuscated'; + } +} + +extension ObfuscateURI on Uri { + /// keeps everything except the base url for security reasons + Uri obfuscate() { + if (!kReleaseMode) { + return this; + } + + // do not obfuscate the local host + if ([null, 'localhost'].contains(host)) { + return this; + } + + // do not obfuscate file urls + if (scheme == 'file') { + return this; + } + + return replace( + userInfo: userInfo == '' ? '' : 'userInfoObfuscated', + host: 'hostObfuscated', + ); + } +} + +extension ObfuscateList on List { + List obfuscate() { + return map((e) { + if (e is AuthenticatedUser) { + return e.obfuscate() as T; + } else if (e is AudiobookShelfServer) { + return e.obfuscate() as T; + } else if (e is Uri) { + return e.obfuscate() as T; + } else { + return e; + } + }).toList(); + } +} + +extension ObfuscateSet on Set { + Set obfuscate() { + return toList().obfuscate().toSet(); + } +} + +extension ObfuscateAuthenticatedUser on AuthenticatedUser { + AuthenticatedUser obfuscate() { + if (!kReleaseMode) { + return this; + } + return copyWith( + username: username == null ? null : 'usernameObfuscated', + authToken: 'authTokenObfuscated', + server: server.obfuscate(), + ); + } +} + +extension ObfuscateServer on AudiobookShelfServer { + AudiobookShelfServer obfuscate() { + if (!kReleaseMode) { + return this; + } + return copyWith(serverUrl: serverUrl.obfuscate()); + } +} + +extension ObfuscateApiSettings on ApiSettings { + ApiSettings obfuscate() { + if (!kReleaseMode) { + return this; + } + return copyWith( + activeServer: activeServer?.obfuscate(), + activeUser: activeUser?.obfuscate(), + ); + } +} + +extension ObfuscateRequest on http.BaseRequest { + http.BaseRequest obfuscate() { + if (!kReleaseMode) { + return this; + } + return http.Request(method, url.obfuscate()); + } +} + +extension ObfuscateResponse on http.Response { + http.Response obfuscate() { + if (!kReleaseMode) { + return this; + } + return http.Response( + obfuscateBody(), + statusCode, + headers: headers, + request: request?.obfuscate(), + ); + } + + String obfuscateBody() { + if (!kReleaseMode) { + return body; + } + // replace any email addresses with emailObfuscated + // replace any phone numbers with phoneObfuscated + // replace any urls with urlObfuscated + // replace any tokens with tokenObfuscated + // token regex is `"token": "..."` + return body + .replaceAll( + RegExp( + r'(\b\w+@\w+\.\w+\b)|' + r'(\b\d{3}-\d{3}-\d{4}\b)|' + r'(\bhttps?://\S+\b)', + ), + 'obfuscated', + ) + .replaceAll( + RegExp(r'"?token"?:?\s*"[^"]+"'), + '"token": "tokenObfuscated"', + ); + } +} + +extension ObfuscateLoginResponse on shelfsdk.LoginResponse { + shelfsdk.LoginResponse obfuscate() { + if (!kReleaseMode) { + return this; + } + return copyWith(user: user.obfuscate()); + } +} + +extension ObfuscateUser on shelfsdk.User { + shelfsdk.User obfuscate() { + if (!kReleaseMode) { + return this; + } + return shelfsdk.User.fromJson(toJson()..['token'] = 'tokenObfuscated'); + } +} diff --git a/lib/shared/extensions/time_of_day.dart b/lib/shared/extensions/time_of_day.dart index 9088b46..a0b0f08 100644 --- a/lib/shared/extensions/time_of_day.dart +++ b/lib/shared/extensions/time_of_day.dart @@ -2,10 +2,7 @@ import 'package:flutter/material.dart'; extension ToTimeOfDay on Duration { TimeOfDay toTimeOfDay() { - return TimeOfDay( - hour: inHours % 24, - minute: inMinutes % 60, - ); + return TimeOfDay(hour: inHours % 24, minute: inMinutes % 60); } } diff --git a/lib/shared/hooks.dart b/lib/shared/hooks.dart index 62c6116..3b9fd8e 100644 --- a/lib/shared/hooks.dart +++ b/lib/shared/hooks.dart @@ -7,85 +7,18 @@ void useInterval(VoidCallback callback, Duration delay) { final savedCallback = useRef(callback); savedCallback.value = callback; - useEffect( - () { - final timer = Timer.periodic(delay, (_) => savedCallback.value()); - return timer.cancel; - }, - [delay], - ); + useEffect(() { + final timer = Timer.periodic(delay, (_) => savedCallback.value()); + return timer.cancel; + }, [delay]); } void useTimer(VoidCallback callback, Duration delay) { final savedCallback = useRef(callback); savedCallback.value = callback; - useEffect( - () { - final timer = Timer(delay, savedCallback.value); - return timer.cancel; - }, - [delay], - ); -} - -/// Creates [FixedExtentScrollController] that will be disposed automatically. -/// -/// See also: -/// - [FixedExtentScrollController] -FixedExtentScrollController useFixedExtentScrollController({ - String? debugLabel, - List? keys, - int initialItem = 0, - void Function(ScrollPosition)? onAttach, - void Function(ScrollPosition)? onDetach, -}) { - return use( - _FixedExtentScrollControllerHook( - debugLabel: debugLabel, - keys: keys, - initialItem: initialItem, - onAttach: onAttach, - onDetach: onDetach, - ), - ); -} - -class _FixedExtentScrollControllerHook - extends Hook { - const _FixedExtentScrollControllerHook({ - this.debugLabel, - super.keys, - required this.initialItem, - this.onAttach, - this.onDetach, - }); - - final int initialItem; - final void Function(ScrollPosition)? onAttach; - final void Function(ScrollPosition)? onDetach; - - final String? debugLabel; - - @override - HookState> - createState() => _FixedExtentScrollControllerHookState(); -} - -class _FixedExtentScrollControllerHookState extends HookState< - FixedExtentScrollController, _FixedExtentScrollControllerHook> { - late final controller = FixedExtentScrollController( - initialItem: hook.initialItem, - onAttach: hook.onAttach, - onDetach: hook.onDetach, - ); - - @override - FixedExtentScrollController build(BuildContext context) => controller; - - @override - void dispose() => controller.dispose(); - - @override - String get debugLabel => 'useFixedExtentScrollController'; + useEffect(() { + final timer = Timer(delay, savedCallback.value); + return timer.cancel; + }, [delay]); } diff --git a/lib/shared/icons/abs_icons.dart b/lib/shared/icons/abs_icons.dart new file mode 100644 index 0000000..ccceb39 --- /dev/null +++ b/lib/shared/icons/abs_icons.dart @@ -0,0 +1,163 @@ +/// Flutter icons AbsIcons +/// Copyright (C) 2025 by original authors @ fluttericon.com, fontello.com +/// This font was generated by FlutterIcon.com, which is derived from Fontello. +/// +/// To use this font, place it in your fonts/ directory and include the +/// following in your pubspec.yaml +/// +/// flutter: +/// fonts: +/// - family: AbsIcons +/// fonts: +/// - asset: fonts/AbsIcons.ttf +/// +/// +/// +library; +// ignore_for_file: constant_identifier_names + +import 'package:flutter/widgets.dart' show IconData; + +class AbsIcons { + AbsIcons._(); + + static const _kFontFam = 'AbsIcons'; + static const String? _kFontPkg = null; + + static const IconData audiobookshelf = IconData( + 0xe900, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData microphone_2 = IconData( + 0xe901, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData microphone_1 = IconData( + 0xe902, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData radio = IconData( + 0xe903, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData podcast = IconData( + 0xe904, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData books_1 = IconData( + 0xe905, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData database_2 = IconData( + 0xe906, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData headphones = IconData( + 0xe910, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData music = IconData( + 0xe911, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData video = IconData( + 0xe914, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData microphone_3 = IconData( + 0xe91e, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData book = IconData( + 0xe91f, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData books_2 = IconData( + 0xe920, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData file_picture = IconData( + 0xe927, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData database_1 = IconData( + 0xe964, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData rocket = IconData( + 0xe9a5, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData power = IconData( + 0xe9b5, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData star = IconData( + 0xe9d9, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData heart = IconData( + 0xe9da, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + static const IconData rss = IconData( + 0xea9b, + fontFamily: _kFontFam, + fontPackage: _kFontPkg, + ); + + static final Map _iconMap = { + 'audiobookshelf': audiobookshelf, + 'microphone_2': microphone_2, + 'microphone_1': microphone_1, + 'radio': radio, + 'podcast': podcast, + 'books_1': books_1, + 'database_2': database_2, + 'headphones': headphones, + 'music': music, + 'video': video, + 'microphone_3': microphone_3, + 'book': book, + 'books_2': books_2, + 'file_picture': file_picture, + 'database_1': database_1, + 'rocket': rocket, + 'power': power, + 'star': star, + 'heart': heart, + 'rss': rss, + }; + + /// Returns the IconData corresponding to the [iconName] string. + /// + /// If the [iconName] is not found in the map, returns null. + /// Considers null or empty strings as invalid. + static IconData? getIconByName(String? iconName) { + if (iconName == null || iconName.isEmpty) { + return null; + } + return _iconMap[iconName.toLowerCase()]; + } + + static Map get iconMap => _iconMap; +} diff --git a/lib/shared/utils.dart b/lib/shared/utils.dart index 6fce1a0..3d600b4 100644 --- a/lib/shared/utils.dart +++ b/lib/shared/utils.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/lib/shared/widgets/add_new_server.dart b/lib/shared/widgets/add_new_server.dart index c04d8fd..450874d 100644 --- a/lib/shared/widgets/add_new_server.dart +++ b/lib/shared/widgets/add_new_server.dart @@ -2,6 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:vaani/api/api_provider.dart'; +import 'package:vaani/main.dart'; + +final httpUrlRegExp = RegExp('https?://'); class AddNewServer extends HookConsumerWidget { const AddNewServer({ @@ -25,7 +28,8 @@ class AddNewServer extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final myController = controller ?? useTextEditingController(); + final myController = + controller ?? useTextEditingController(text: 'https://'); var newServerURI = useValueListenable(myController); final isServerAlive = ref.watch(isServerAliveProvider(newServerURI.text)); bool isServerAliveValue = isServerAlive.when( @@ -34,21 +38,44 @@ class AddNewServer extends HookConsumerWidget { error: (error, _) => false, ); + Uri parsedUri = Uri.parse(''); + + try { + parsedUri = Uri.parse(newServerURI.text); + } on FormatException { + // prepend https:// if not present + if (!newServerURI.text.startsWith(httpUrlRegExp)) { + myController.text = 'https://${newServerURI.text}'; + parsedUri = Uri.parse(myController.text); + } + } catch (e) { + // do nothing + appLogger.severe('Error parsing URI: $e'); + } + final canSubmit = + !readOnly && + (isServerAliveValue || (allowEmpty && newServerURI.text.isEmpty)); return TextFormField( readOnly: readOnly, controller: controller, keyboardType: TextInputType.url, autofillHints: const [AutofillHints.url], - textInputAction: TextInputAction.next, + textInputAction: TextInputAction.done, + onFieldSubmitted: canSubmit + ? (_) { + onPressed?.call(); + } + : null, decoration: InputDecoration( labelText: 'Server URI', labelStyle: TextStyle( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8), + color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.8), ), border: const OutlineInputBorder(), - prefixText: - myController.text.startsWith(RegExp('https?://')) ? '' : 'https://', - prefixIcon: ServerAliveIcon(server: Uri.parse(newServerURI.text)), + prefixText: myController.text.startsWith(httpUrlRegExp) + ? '' + : 'https://', + prefixIcon: ServerAliveIcon(server: parsedUri), // add server button suffixIcon: onPressed == null @@ -62,10 +89,10 @@ class AddNewServer extends HookConsumerWidget { focusColor: Theme.of(context).colorScheme.onSurface, // should be enabled when - onPressed: !readOnly && - (isServerAliveValue || - (allowEmpty && newServerURI.text.isEmpty)) - ? onPressed + onPressed: canSubmit + ? () { + onPressed?.call(); + } : null, // disable button if server is not alive ), ), @@ -76,10 +103,7 @@ class AddNewServer extends HookConsumerWidget { } class ServerAliveIcon extends HookConsumerWidget { - const ServerAliveIcon({ - super.key, - required this.server, - }); + const ServerAliveIcon({super.key, required this.server}); final Uri server; @@ -96,8 +120,8 @@ class ServerAliveIcon extends HookConsumerWidget { message: server.toString().isEmpty ? 'Server Status' : isServerAliveValue - ? 'Server connected' - : 'Cannot connect to server', + ? 'Server connected' + : 'Cannot connect to server', child: server.toString().isEmpty ? Icon( Icons.cloud_outlined, diff --git a/lib/shared/widgets/drawer.dart b/lib/shared/widgets/drawer.dart index be224e9..56bec1f 100644 --- a/lib/shared/widgets/drawer.dart +++ b/lib/shared/widgets/drawer.dart @@ -3,11 +3,8 @@ import 'package:go_router/go_router.dart'; import 'package:vaani/features/you/view/server_manager.dart'; import 'package:vaani/router/router.dart'; - class MyDrawer extends StatelessWidget { - const MyDrawer({ - super.key, - }); + const MyDrawer({super.key}); @override Widget build(BuildContext context) { @@ -17,10 +14,7 @@ class MyDrawer extends StatelessWidget { const DrawerHeader( child: Text( 'Vaani', - style: TextStyle( - fontStyle: FontStyle.italic, - fontSize: 30, - ), + style: TextStyle(fontStyle: FontStyle.italic, fontSize: 30), ), ), ListTile( diff --git a/lib/shared/widgets/expandable_description.dart b/lib/shared/widgets/expandable_description.dart index 06605e4..bc2cee9 100644 --- a/lib/shared/widgets/expandable_description.dart +++ b/lib/shared/widgets/expandable_description.dart @@ -39,7 +39,7 @@ class ExpandableDescription extends HookWidget { // header with carrot icon is tapable InkWell( borderRadius: BorderRadius.circular(8), - + onTap: () { isDescExpanded.value = !isDescExpanded.value; if (isDescExpanded.value) { @@ -55,10 +55,7 @@ class ExpandableDescription extends HookWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // header text - Text( - style: textTheme.titleMedium, - title, - ), + Text(style: textTheme.titleMedium, title), // carrot icon AnimatedRotation( turns: isDescExpanded.value ? 0.5 : 0, @@ -79,11 +76,7 @@ class ExpandableDescription extends HookWidget { child: AnimatedSwitcher( duration: duration * 3, child: isDescExpanded.value - ? Text( - style: textTheme.bodyMedium, - content, - maxLines: null, - ) + ? Text(style: textTheme.bodyMedium, content, maxLines: null) : Text( style: textTheme.bodyMedium, content, diff --git a/lib/shared/widgets/not_implemented.dart b/lib/shared/widgets/not_implemented.dart index 801f152..5c10a60 100644 --- a/lib/shared/widgets/not_implemented.dart +++ b/lib/shared/widgets/not_implemented.dart @@ -2,9 +2,6 @@ import 'package:flutter/material.dart'; void showNotImplementedToast(BuildContext context) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text("Not implemented"), - showCloseIcon: true, - ), + const SnackBar(content: Text("Not implemented"), showCloseIcon: true), ); } diff --git a/lib/shared/widgets/shelves/author_shelf.dart b/lib/shared/widgets/shelves/author_shelf.dart index aa0acce..36dbadf 100644 --- a/lib/shared/widgets/shelves/author_shelf.dart +++ b/lib/shared/widgets/shelves/author_shelf.dart @@ -6,11 +6,7 @@ import 'package:vaani/shared/widgets/shelves/home_shelf.dart'; /// A shelf that displays Authors on the home page class AuthorHomeShelf extends HookConsumerWidget { - const AuthorHomeShelf({ - super.key, - required this.shelf, - required this.title, - }); + const AuthorHomeShelf({super.key, required this.shelf, required this.title}); final String title; final AuthorShelf shelf; @@ -20,9 +16,7 @@ class AuthorHomeShelf extends HookConsumerWidget { return SimpleHomeShelf( title: title, children: shelf.entities - .map( - (item) => AuthorOnShelf(item: item), - ) + .map((item) => AuthorOnShelf(item: item)) .toList(), ); } @@ -30,10 +24,7 @@ class AuthorHomeShelf extends HookConsumerWidget { // a widget to display a item on the shelf class AuthorOnShelf extends HookConsumerWidget { - const AuthorOnShelf({ - super.key, - required this.item, - }); + const AuthorOnShelf({super.key, required this.item}); final Author item; diff --git a/lib/shared/widgets/shelves/book_shelf.dart b/lib/shared/widgets/shelves/book_shelf.dart index 692d3b3..0919161 100644 --- a/lib/shared/widgets/shelves/book_shelf.dart +++ b/lib/shared/widgets/shelves/book_shelf.dart @@ -17,7 +17,7 @@ import 'package:vaani/router/router.dart'; import 'package:vaani/settings/app_settings_provider.dart'; import 'package:vaani/shared/extensions/model_conversions.dart'; import 'package:vaani/shared/widgets/shelves/home_shelf.dart'; -import 'package:vaani/theme/theme_from_cover_provider.dart'; +import 'package:vaani/theme/providers/theme_from_cover_provider.dart'; /// A shelf that displays books on the home page class BookHomeShelf extends HookConsumerWidget { @@ -25,10 +25,12 @@ class BookHomeShelf extends HookConsumerWidget { super.key, required this.shelf, required this.title, + this.showPlayButton = false, }); final String title; final LibraryItemShelf shelf; + final bool showPlayButton; @override Widget build(BuildContext context, WidgetRef ref) { @@ -38,10 +40,11 @@ class BookHomeShelf extends HookConsumerWidget { .map( (item) => switch (item.mediaType) { MediaType.book => BookOnShelf( - item: item, - key: ValueKey(shelf.id + item.id), - heroTagSuffix: shelf.id, - ), + item: item, + key: ValueKey(shelf.id + item.id), + heroTagSuffix: shelf.id, + showPlayButton: showPlayButton, + ), _ => Container(), }, ) @@ -80,13 +83,8 @@ class BookOnShelf extends HookConsumerWidget { // open the book context.pushNamed( Routes.libraryItem.name, - pathParameters: { - Routes.libraryItem.pathParamName!: item.id, - }, - extra: LibraryItemExtras( - book: book, - heroTagSuffix: heroTagSuffix, - ), + pathParameters: {Routes.libraryItem.pathParamName!: item.id}, + extra: LibraryItemExtras(book: book, heroTagSuffix: heroTagSuffix), ); } @@ -96,8 +94,11 @@ class BookOnShelf extends HookConsumerWidget { onTap: handleTapOnBook, borderRadius: BorderRadius.circular(10), child: Padding( - padding: - const EdgeInsets.only(bottom: 8.0, right: 4.0, left: 4.0), + padding: const EdgeInsets.only( + bottom: 8.0, + right: 4.0, + left: 4.0, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -109,51 +110,55 @@ class BookOnShelf extends HookConsumerWidget { alignment: Alignment.bottomRight, children: [ Hero( - tag: HeroTagPrefixes.bookCover + + tag: + HeroTagPrefixes.bookCover + item.id + heroTagSuffix, child: ClipRRect( borderRadius: BorderRadius.circular(10), - child: coverImage.when( - data: (image) { - // return const BookCoverSkeleton(); - if (image.isEmpty) { + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: coverImage.when( + data: (image) { + // return const BookCoverSkeleton(); + if (image.isEmpty) { + return const Icon(Icons.error); + } + var imageWidget = Image.memory( + image, + fit: BoxFit.fill, + cacheWidth: + (height * + 1.2 * + MediaQuery.of( + context, + ).devicePixelRatio) + .round(), + ); + return Container( + decoration: BoxDecoration( + color: Theme.of( + context, + ).colorScheme.onPrimaryContainer, + ), + child: imageWidget, + ); + }, + loading: () { + return const Center( + child: BookCoverSkeleton(), + ); + }, + error: (error, stack) { return const Icon(Icons.error); - } - var imageWidget = Image.memory( - image, - fit: BoxFit.fill, - cacheWidth: (height * - 1.2 * - MediaQuery.of(context) - .devicePixelRatio) - .round(), - ); - return Container( - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .onPrimaryContainer, - ), - child: imageWidget, - ); - }, - loading: () { - return const Center( - child: BookCoverSkeleton(), - ); - }, - error: (error, stack) { - return const Icon(Icons.error); - }, + }, + ), ), ), ), // a play button on the book cover if (showPlayButton) - _BookOnShelfPlayButton( - libraryItemId: item.id, - ), + _BookOnShelfPlayButton(libraryItemId: item.id), ], ), ), @@ -196,10 +201,7 @@ class BookOnShelf extends HookConsumerWidget { } class _BookOnShelfPlayButton extends HookConsumerWidget { - const _BookOnShelfPlayButton({ - super.key, - required this.libraryItemId, - }); + const _BookOnShelfPlayButton({required this.libraryItemId}); /// the id of the library item of the book final String libraryItemId; @@ -212,8 +214,9 @@ class _BookOnShelfPlayButton extends HookConsumerWidget { player.book?.libraryItemId == libraryItemId; final isPlayingThisBook = player.playing && isCurrentBookSetInPlayer; - final userProgress = me.valueOrNull?.mediaProgress - ?.firstWhereOrNull((element) => element.libraryItemId == libraryItemId); + final userProgress = me.value?.mediaProgress?.firstWhereOrNull( + (element) => element.libraryItemId == libraryItemId, + ); final isBookCompleted = userProgress?.isFinished ?? false; const size = 40.0; @@ -221,8 +224,10 @@ class _BookOnShelfPlayButton extends HookConsumerWidget { // if there is user progress for this book show a circular progress indicator around the play button var strokeWidth = size / 8; - final useMaterialThemeOnItemPage = - ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage; + final useMaterialThemeOnItemPage = ref + .watch(appSettingsProvider) + .themeSettings + .useMaterialThemeOnItemPage; AsyncValue coverColorScheme = const AsyncValue.loading(); if (useMaterialThemeOnItemPage && isCurrentBookSetInPlayer) { @@ -237,8 +242,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget { return Theme( // if current book is set in player, get theme from the cover image data: ThemeData( - colorScheme: - coverColorScheme.valueOrNull ?? Theme.of(context).colorScheme, + colorScheme: coverColorScheme.value ?? Theme.of(context).colorScheme, ), child: Padding( padding: EdgeInsets.all(strokeWidth / 2 + 2), @@ -253,8 +257,9 @@ class _BookOnShelfPlayButton extends HookConsumerWidget { child: CircularProgressIndicator( value: userProgress.progress, strokeWidth: strokeWidth, - backgroundColor: - Theme.of(context).colorScheme.onPrimary.withOpacity(0.8), + backgroundColor: Theme.of( + context, + ).colorScheme.onPrimary.withValues(alpha: 0.8), valueColor: AlwaysStoppedAnimation( Theme.of(context).colorScheme.primary, ), @@ -265,19 +270,18 @@ class _BookOnShelfPlayButton extends HookConsumerWidget { IconButton( color: Theme.of(context).colorScheme.primary, style: ButtonStyle( - padding: WidgetStateProperty.all( - EdgeInsets.zero, - ), - minimumSize: WidgetStateProperty.all( - const Size(size, size), - ), + padding: WidgetStateProperty.all(EdgeInsets.zero), + minimumSize: WidgetStateProperty.all(const Size(size, size)), backgroundColor: WidgetStateProperty.all( - Theme.of(context).colorScheme.onPrimary.withOpacity(0.9), + Theme.of( + context, + ).colorScheme.onPrimary.withValues(alpha: 0.9), ), ), onPressed: () async { - final book = - await ref.watch(libraryItemProvider(libraryItemId).future); + final book = await ref.watch( + libraryItemProvider(libraryItemId).future, + ); libraryItemPlayButtonOnPressed( ref: ref, @@ -303,9 +307,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget { // a skeleton for the book cover class BookCoverSkeleton extends StatelessWidget { - const BookCoverSkeleton({ - super.key, - }); + const BookCoverSkeleton({super.key}); @override Widget build(BuildContext context) { @@ -314,12 +316,13 @@ class BookCoverSkeleton extends StatelessWidget { child: SizedBox( width: 150, child: Shimmer.fromColors( - baseColor: Theme.of(context).colorScheme.surface.withOpacity(0.3), - highlightColor: - Theme.of(context).colorScheme.onSurface.withOpacity(0.1), - child: Container( - color: Theme.of(context).colorScheme.surface, - ), + baseColor: Theme.of( + context, + ).colorScheme.surface.withValues(alpha: 0.3), + highlightColor: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.1), + child: Container(color: Theme.of(context).colorScheme.surface), ), ), ); diff --git a/lib/shared/widgets/shelves/home_shelf.dart b/lib/shared/widgets/shelves/home_shelf.dart index ed22f8d..3770fe1 100644 --- a/lib/shared/widgets/shelves/home_shelf.dart +++ b/lib/shared/widgets/shelves/home_shelf.dart @@ -15,22 +15,25 @@ class HomeShelf extends HookConsumerWidget { super.key, required this.shelf, required this.title, + this.showPlayButton = false, }); final String title; final Shelf shelf; + final bool showPlayButton; @override Widget build(BuildContext context, WidgetRef ref) { return switch (shelf.type) { ShelfType.book => BookHomeShelf( - title: title, - shelf: shelf.asLibraryItemShelf, - ), + title: title, + shelf: shelf.asLibraryItemShelf, + showPlayButton: showPlayButton, + ), ShelfType.authors => AuthorHomeShelf( - title: title, - shelf: shelf.asAuthorShelf, - ), + title: title, + shelf: shelf.asAuthorShelf, + ), _ => Container(), }; } @@ -72,9 +75,7 @@ class SimpleHomeShelf extends HookConsumerWidget { scrollDirection: Axis.horizontal, itemBuilder: (context, index) { if (index == 0 || index == children.length + 1) { - return const SizedBox( - width: 8, - ); + return const SizedBox(width: 8); } return children[index - 1]; }, @@ -85,7 +86,8 @@ class SimpleHomeShelf extends HookConsumerWidget { return const SizedBox(width: 4); }, - itemCount: children.length + + itemCount: + children.length + 2, // add some extra space at the start and end so that the first and last items are not at the edge ), ), diff --git a/lib/shared/widgets/vaani_logo.dart b/lib/shared/widgets/vaani_logo.dart new file mode 100644 index 0000000..a5b4e11 --- /dev/null +++ b/lib/shared/widgets/vaani_logo.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class VaaniLogo extends StatelessWidget { + const VaaniLogo({ + super.key, + this.size, + this.duration = const Duration(milliseconds: 750), + this.curve = Curves.fastOutSlowIn, + }); + + final double? size; + final Duration duration; + final Curve curve; + + @override + Widget build(BuildContext context) { + final IconThemeData iconTheme = IconTheme.of(context); + final double? iconSize = size ?? iconTheme.size; + return AnimatedContainer( + width: iconSize, + height: iconSize, + duration: duration, + curve: curve, + child: Image.asset('assets/images/vaani_logo_foreground.png'), + ); + } +} diff --git a/lib/theme/dark.dart b/lib/theme/dark.dart deleted file mode 100644 index 5473632..0000000 --- a/lib/theme/dark.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:vaani/theme/theme.dart'; - -final ThemeData darkTheme = ThemeData( - brightness: Brightness.dark, - colorScheme: ColorScheme.fromSeed( - seedColor: brandColor, - brightness: Brightness.dark, - ), -); diff --git a/lib/theme/light.dart b/lib/theme/light.dart deleted file mode 100644 index aa6ba4d..0000000 --- a/lib/theme/light.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:vaani/theme/theme.dart'; - -final ThemeData lightTheme = ThemeData( - brightness: Brightness.light, - colorScheme: ColorScheme.fromSeed( - seedColor: brandColor, - brightness: Brightness.light, - ), -); diff --git a/lib/theme/providers/system_theme_provider.dart b/lib/theme/providers/system_theme_provider.dart new file mode 100644 index 0000000..cc01415 --- /dev/null +++ b/lib/theme/providers/system_theme_provider.dart @@ -0,0 +1,67 @@ +import 'package:dynamic_color/dynamic_color.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:logging/logging.dart'; +import 'package:material_color_utilities/material_color_utilities.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'system_theme_provider.g.dart'; + +final _logger = Logger('SystemThemeProvider'); + +/// copied from [DynamicColorBuilder] +@Riverpod(keepAlive: true) +FutureOr<(ColorScheme light, ColorScheme dark)?> systemTheme( + Ref ref, { + bool highContrast = false, +}) async { + _logger.fine('Generating system theme'); + ColorScheme? schemeLight; + ColorScheme? schemeDark; + // Platform messages may fail, so we use a try/catch PlatformException. + try { + CorePalette? corePalette = await DynamicColorPlugin.getCorePalette(); + + if (corePalette != null) { + _logger.fine('dynamic_color: Core palette detected.'); + schemeLight = corePalette.toColorScheme(brightness: Brightness.light); + schemeDark = corePalette.toColorScheme(brightness: Brightness.dark); + } + } on PlatformException { + _logger.warning('dynamic_color: Failed to obtain core palette.'); + } + + if (schemeLight == null || schemeDark == null) { + try { + final Color? accentColor = await DynamicColorPlugin.getAccentColor(); + + if (accentColor != null) { + _logger.fine('dynamic_color: Accent color detected.'); + schemeLight = ColorScheme.fromSeed( + seedColor: accentColor, + brightness: Brightness.light, + ); + schemeDark = ColorScheme.fromSeed( + seedColor: accentColor, + brightness: Brightness.dark, + ); + } + } on PlatformException { + _logger.warning('dynamic_color: Failed to obtain accent color.'); + } + } + + if (schemeLight == null || schemeDark == null) { + _logger.warning( + 'dynamic_color: Dynamic color not detected on this device.', + ); + return null; + } + // set high contrast theme + if (highContrast) { + schemeLight = schemeLight.copyWith(surface: Colors.white).harmonized(); + schemeDark = schemeDark.copyWith(surface: Colors.black).harmonized(); + } + return (schemeLight, schemeDark); +} diff --git a/lib/theme/providers/system_theme_provider.g.dart b/lib/theme/providers/system_theme_provider.g.dart new file mode 100644 index 0000000..a28f0f0 --- /dev/null +++ b/lib/theme/providers/system_theme_provider.g.dart @@ -0,0 +1,96 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'system_theme_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// copied from [DynamicColorBuilder] + +@ProviderFor(systemTheme) +final systemThemeProvider = SystemThemeFamily._(); + +/// copied from [DynamicColorBuilder] + +final class SystemThemeProvider + extends + $FunctionalProvider< + AsyncValue<(ColorScheme, ColorScheme)?>, + (ColorScheme, ColorScheme)?, + FutureOr<(ColorScheme, ColorScheme)?> + > + with + $FutureModifier<(ColorScheme, ColorScheme)?>, + $FutureProvider<(ColorScheme, ColorScheme)?> { + /// copied from [DynamicColorBuilder] + SystemThemeProvider._({ + required SystemThemeFamily super.from, + required bool super.argument, + }) : super( + retry: null, + name: r'systemThemeProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$systemThemeHash(); + + @override + String toString() { + return r'systemThemeProvider' + '' + '($argument)'; + } + + @$internal + @override + $FutureProviderElement<(ColorScheme, ColorScheme)?> $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr<(ColorScheme, ColorScheme)?> create(Ref ref) { + final argument = this.argument as bool; + return systemTheme(ref, highContrast: argument); + } + + @override + bool operator ==(Object other) { + return other is SystemThemeProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$systemThemeHash() => r'c78d3d94683624a80b296594268c5fd4295e77a3'; + +/// copied from [DynamicColorBuilder] + +final class SystemThemeFamily extends $Family + with + $FunctionalFamilyOverride, bool> { + SystemThemeFamily._() + : super( + retry: null, + name: r'systemThemeProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: false, + ); + + /// copied from [DynamicColorBuilder] + + SystemThemeProvider call({bool highContrast = false}) => + SystemThemeProvider._(argument: highContrast, from: this); + + @override + String toString() => r'systemThemeProvider'; +} diff --git a/lib/theme/theme_from_cover_provider.dart b/lib/theme/providers/theme_from_cover_provider.dart similarity index 78% rename from lib/theme/theme_from_cover_provider.dart rename to lib/theme/providers/theme_from_cover_provider.dart index a535f2f..3e9a667 100644 --- a/lib/theme/theme_from_cover_provider.dart +++ b/lib/theme/providers/theme_from_cover_provider.dart @@ -1,5 +1,7 @@ +import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:vaani/api/image_provider.dart'; @@ -10,18 +12,28 @@ final _logger = Logger('ThemeFromCoverProvider'); @Riverpod(keepAlive: true) Future> themeFromCover( - ThemeFromCoverRef ref, + Ref ref, ImageProvider img, { Brightness brightness = Brightness.dark, + bool highContrast = false, }) async { // ! add deliberate delay to simulate a long running task as it interferes with other animations await Future.delayed(500.ms); _logger.fine('Generating color scheme from cover image'); - return ColorScheme.fromImageProvider( + var theme = await ColorScheme.fromImageProvider( provider: img, brightness: brightness, ); + // set high contrast theme + if (highContrast) { + theme = theme + .copyWith( + surface: brightness == Brightness.light ? Colors.white : Colors.black, + ) + .harmonized(); + } + return theme; // TODO isolate is not working // see https://github.com/flutter/flutter/issues/119207 // use isolate to generate the color scheme @@ -47,17 +59,21 @@ Future> themeFromCover( @Riverpod(keepAlive: true) FutureOr themeOfLibraryItem( - ThemeOfLibraryItemRef ref, + Ref ref, String? itemId, { Brightness brightness = Brightness.dark, + bool highContrast = false, }) async { if (itemId == null) { return null; } final coverImage = await ref.watch(coverImageProvider(itemId).future); final val = await ref.watch( - themeFromCoverProvider(MemoryImage(coverImage), brightness: brightness) - .future, + themeFromCoverProvider( + MemoryImage(coverImage), + brightness: brightness, + highContrast: highContrast, + ).future, ); return val; // coverImage.when( diff --git a/lib/theme/providers/theme_from_cover_provider.g.dart b/lib/theme/providers/theme_from_cover_provider.g.dart new file mode 100644 index 0000000..844e608 --- /dev/null +++ b/lib/theme/providers/theme_from_cover_provider.g.dart @@ -0,0 +1,202 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'theme_from_cover_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(themeFromCover) +final themeFromCoverProvider = ThemeFromCoverFamily._(); + +final class ThemeFromCoverProvider + extends + $FunctionalProvider< + AsyncValue>, + FutureOr, + FutureOr> + > + with + $FutureModifier>, + $FutureProvider> { + ThemeFromCoverProvider._({ + required ThemeFromCoverFamily super.from, + required (ImageProvider, {Brightness brightness, bool highContrast}) + super.argument, + }) : super( + retry: null, + name: r'themeFromCoverProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$themeFromCoverHash(); + + @override + String toString() { + return r'themeFromCoverProvider' + '' + '$argument'; + } + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr> create(Ref ref) { + final argument = + this.argument + as ( + ImageProvider, { + Brightness brightness, + bool highContrast, + }); + return themeFromCover( + ref, + argument.$1, + brightness: argument.brightness, + highContrast: argument.highContrast, + ); + } + + @override + bool operator ==(Object other) { + return other is ThemeFromCoverProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$themeFromCoverHash() => r'afdeddc4bfe2fe46a4185143d3a88a23565e33f4'; + +final class ThemeFromCoverFamily extends $Family + with + $FunctionalFamilyOverride< + FutureOr>, + (ImageProvider, {Brightness brightness, bool highContrast}) + > { + ThemeFromCoverFamily._() + : super( + retry: null, + name: r'themeFromCoverProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: false, + ); + + ThemeFromCoverProvider call( + ImageProvider img, { + Brightness brightness = Brightness.dark, + bool highContrast = false, + }) => ThemeFromCoverProvider._( + argument: (img, brightness: brightness, highContrast: highContrast), + from: this, + ); + + @override + String toString() => r'themeFromCoverProvider'; +} + +@ProviderFor(themeOfLibraryItem) +final themeOfLibraryItemProvider = ThemeOfLibraryItemFamily._(); + +final class ThemeOfLibraryItemProvider + extends + $FunctionalProvider< + AsyncValue, + ColorScheme?, + FutureOr + > + with $FutureModifier, $FutureProvider { + ThemeOfLibraryItemProvider._({ + required ThemeOfLibraryItemFamily super.from, + required (String?, {Brightness brightness, bool highContrast}) + super.argument, + }) : super( + retry: null, + name: r'themeOfLibraryItemProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$themeOfLibraryItemHash(); + + @override + String toString() { + return r'themeOfLibraryItemProvider' + '' + '$argument'; + } + + @$internal + @override + $FutureProviderElement $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + final argument = + this.argument as (String?, {Brightness brightness, bool highContrast}); + return themeOfLibraryItem( + ref, + argument.$1, + brightness: argument.brightness, + highContrast: argument.highContrast, + ); + } + + @override + bool operator ==(Object other) { + return other is ThemeOfLibraryItemProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$themeOfLibraryItemHash() => + r'0b2df397b2938003a9de6beb6d4204401a05370c'; + +final class ThemeOfLibraryItemFamily extends $Family + with + $FunctionalFamilyOverride< + FutureOr, + (String?, {Brightness brightness, bool highContrast}) + > { + ThemeOfLibraryItemFamily._() + : super( + retry: null, + name: r'themeOfLibraryItemProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: false, + ); + + ThemeOfLibraryItemProvider call( + String? itemId, { + Brightness brightness = Brightness.dark, + bool highContrast = false, + }) => ThemeOfLibraryItemProvider._( + argument: (itemId, brightness: brightness, highContrast: highContrast), + from: this, + ); + + @override + String toString() => r'themeOfLibraryItemProvider'; +} diff --git a/lib/theme/theme.dart b/lib/theme/theme.dart index 72e81b1..35f4ad8 100644 --- a/lib/theme/theme.dart +++ b/lib/theme/theme.dart @@ -1,9 +1,15 @@ -import 'dart:ui'; - -export 'dark.dart'; -export 'light.dart'; - +import 'package:flutter/material.dart'; // brand color rgb(49, 27, 146) rgb(96, 76, 236) const brandColor = Color(0xFF311B92); const brandColorLight = Color(0xFF604CEC); + +final brandLightColorScheme = ColorScheme.fromSeed( + seedColor: brandColor, + brightness: Brightness.light, +); + +final brandDarkColorScheme = ColorScheme.fromSeed( + seedColor: brandColor, + brightness: Brightness.dark, +); diff --git a/lib/theme/theme_from_cover_provider.g.dart b/lib/theme/theme_from_cover_provider.g.dart deleted file mode 100644 index 61a86a9..0000000 --- a/lib/theme/theme_from_cover_provider.g.dart +++ /dev/null @@ -1,324 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'theme_from_cover_provider.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$themeFromCoverHash() => r'a549513a0dcdff76be94488baf38a8b886ce63eb'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [themeFromCover]. -@ProviderFor(themeFromCover) -const themeFromCoverProvider = ThemeFromCoverFamily(); - -/// See also [themeFromCover]. -class ThemeFromCoverFamily extends Family>> { - /// See also [themeFromCover]. - const ThemeFromCoverFamily(); - - /// See also [themeFromCover]. - ThemeFromCoverProvider call( - ImageProvider img, { - Brightness brightness = Brightness.dark, - }) { - return ThemeFromCoverProvider( - img, - brightness: brightness, - ); - } - - @override - ThemeFromCoverProvider getProviderOverride( - covariant ThemeFromCoverProvider provider, - ) { - return call( - provider.img, - brightness: provider.brightness, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'themeFromCoverProvider'; -} - -/// See also [themeFromCover]. -class ThemeFromCoverProvider extends FutureProvider> { - /// See also [themeFromCover]. - ThemeFromCoverProvider( - ImageProvider img, { - Brightness brightness = Brightness.dark, - }) : this._internal( - (ref) => themeFromCover( - ref as ThemeFromCoverRef, - img, - brightness: brightness, - ), - from: themeFromCoverProvider, - name: r'themeFromCoverProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$themeFromCoverHash, - dependencies: ThemeFromCoverFamily._dependencies, - allTransitiveDependencies: - ThemeFromCoverFamily._allTransitiveDependencies, - img: img, - brightness: brightness, - ); - - ThemeFromCoverProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.img, - required this.brightness, - }) : super.internal(); - - final ImageProvider img; - final Brightness brightness; - - @override - Override overrideWith( - FutureOr> Function(ThemeFromCoverRef provider) - create, - ) { - return ProviderOverride( - origin: this, - override: ThemeFromCoverProvider._internal( - (ref) => create(ref as ThemeFromCoverRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - img: img, - brightness: brightness, - ), - ); - } - - @override - FutureProviderElement> createElement() { - return _ThemeFromCoverProviderElement(this); - } - - @override - bool operator ==(Object other) { - return other is ThemeFromCoverProvider && - other.img == img && - other.brightness == brightness; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, img.hashCode); - hash = _SystemHash.combine(hash, brightness.hashCode); - - return _SystemHash.finish(hash); - } -} - -mixin ThemeFromCoverRef on FutureProviderRef> { - /// The parameter `img` of this provider. - ImageProvider get img; - - /// The parameter `brightness` of this provider. - Brightness get brightness; -} - -class _ThemeFromCoverProviderElement - extends FutureProviderElement> - with ThemeFromCoverRef { - _ThemeFromCoverProviderElement(super.provider); - - @override - ImageProvider get img => (origin as ThemeFromCoverProvider).img; - @override - Brightness get brightness => (origin as ThemeFromCoverProvider).brightness; -} - -String _$themeOfLibraryItemHash() => - r'a1d0e5d81f4debe88d5a6ce46c3af28623ad4273'; - -/// See also [themeOfLibraryItem]. -@ProviderFor(themeOfLibraryItem) -const themeOfLibraryItemProvider = ThemeOfLibraryItemFamily(); - -/// See also [themeOfLibraryItem]. -class ThemeOfLibraryItemFamily extends Family> { - /// See also [themeOfLibraryItem]. - const ThemeOfLibraryItemFamily(); - - /// See also [themeOfLibraryItem]. - ThemeOfLibraryItemProvider call( - String? itemId, { - Brightness brightness = Brightness.dark, - }) { - return ThemeOfLibraryItemProvider( - itemId, - brightness: brightness, - ); - } - - @override - ThemeOfLibraryItemProvider getProviderOverride( - covariant ThemeOfLibraryItemProvider provider, - ) { - return call( - provider.itemId, - brightness: provider.brightness, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'themeOfLibraryItemProvider'; -} - -/// See also [themeOfLibraryItem]. -class ThemeOfLibraryItemProvider extends FutureProvider { - /// See also [themeOfLibraryItem]. - ThemeOfLibraryItemProvider( - String? itemId, { - Brightness brightness = Brightness.dark, - }) : this._internal( - (ref) => themeOfLibraryItem( - ref as ThemeOfLibraryItemRef, - itemId, - brightness: brightness, - ), - from: themeOfLibraryItemProvider, - name: r'themeOfLibraryItemProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$themeOfLibraryItemHash, - dependencies: ThemeOfLibraryItemFamily._dependencies, - allTransitiveDependencies: - ThemeOfLibraryItemFamily._allTransitiveDependencies, - itemId: itemId, - brightness: brightness, - ); - - ThemeOfLibraryItemProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.itemId, - required this.brightness, - }) : super.internal(); - - final String? itemId; - final Brightness brightness; - - @override - Override overrideWith( - FutureOr Function(ThemeOfLibraryItemRef provider) create, - ) { - return ProviderOverride( - origin: this, - override: ThemeOfLibraryItemProvider._internal( - (ref) => create(ref as ThemeOfLibraryItemRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - itemId: itemId, - brightness: brightness, - ), - ); - } - - @override - FutureProviderElement createElement() { - return _ThemeOfLibraryItemProviderElement(this); - } - - @override - bool operator ==(Object other) { - return other is ThemeOfLibraryItemProvider && - other.itemId == itemId && - other.brightness == brightness; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, itemId.hashCode); - hash = _SystemHash.combine(hash, brightness.hashCode); - - return _SystemHash.finish(hash); - } -} - -mixin ThemeOfLibraryItemRef on FutureProviderRef { - /// The parameter `itemId` of this provider. - String? get itemId; - - /// The parameter `brightness` of this provider. - Brightness get brightness; -} - -class _ThemeOfLibraryItemProviderElement - extends FutureProviderElement with ThemeOfLibraryItemRef { - _ThemeOfLibraryItemProviderElement(super.provider); - - @override - String? get itemId => (origin as ThemeOfLibraryItemProvider).itemId; - @override - Brightness get brightness => - (origin as ThemeOfLibraryItemProvider).brightness; -} -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 879195f..f06b290 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,14 +6,18 @@ #include "generated_plugin_registrant.h" -#include +#include +#include #include #include void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin"); - isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar); + g_autoptr(FlPluginRegistrar) dynamic_color_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); + dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); + g_autoptr(FlPluginRegistrar) isar_plus_flutter_libs_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "IsarPlusFlutterLibsPlugin"); + isar_plus_flutter_libs_plugin_register_with_registrar(isar_plus_flutter_libs_registrar); g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 026cbff..17405b8 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,7 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST - isar_flutter_libs + dynamic_color + isar_plus_flutter_libs media_kit_libs_linux url_launcher_linux ) diff --git a/linux/my_application.cc b/linux/my_application.cc index 8a52c8b..4c7da0c 100644 --- a/linux/my_application.cc +++ b/linux/my_application.cc @@ -17,6 +17,14 @@ G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) // Implements GApplication::activate. static void my_application_activate(GApplication* application) { MyApplication* self = MY_APPLICATION(application); + + GList *windows = gtk_application_get_windows(GTK_APPLICATION(application)); + if (windows) + { + gtk_window_present(GTK_WINDOW(windows->data)); + return; + } + GtkWindow* window = GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); @@ -40,11 +48,11 @@ static void my_application_activate(GApplication* application) { if (use_header_bar) { GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "vaani"); + gtk_header_bar_set_title(header_bar, "Vaani"); gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); } else { - gtk_window_set_title(window, "vaani"); + gtk_window_set_title(window, "Vaani"); } gtk_window_set_default_size(window, 1280, 720); @@ -78,7 +86,7 @@ static gboolean my_application_local_command_line(GApplication* application, gch g_application_activate(application); *exit_status = 0; - return TRUE; + return FALSE; } // Implements GApplication::startup. @@ -119,6 +127,6 @@ static void my_application_init(MyApplication* self) {} MyApplication* my_application_new() { return MY_APPLICATION(g_object_new(my_application_get_type(), "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, + "flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN, nullptr)); } diff --git a/linux/packaging/appimage/make_config.yaml b/linux/packaging/appimage/make_config.yaml new file mode 100644 index 0000000..b1183ae --- /dev/null +++ b/linux/packaging/appimage/make_config.yaml @@ -0,0 +1,34 @@ +display_name: Vaani +package_name: vaani + +maintainer: + name: Dr.Blank + email: drblankdev@gmail.com + +priority: optional + +section: x11 + +installed_size: 75700 + +essential: false + +icon: assets/icon/logo.png + +postuninstall_scripts: + - echo "Sorry to see you go." + +keywords: + - Audiobook + - Audiobook Player + - Audiobookshelf + +generic_name: Audiobook Player + +categories: + - AudioVideo + - Audio + - Player + +startup_notify: true +# TODO: Review and update fields for AppImage specifics (e.g., icon, metadata). diff --git a/linux/packaging/deb/make_config.yaml b/linux/packaging/deb/make_config.yaml new file mode 100644 index 0000000..5579be4 --- /dev/null +++ b/linux/packaging/deb/make_config.yaml @@ -0,0 +1,51 @@ +display_name: Vaani +package_name: vaani + +maintainer: + name: Dr.Blank + email: drblankdev@gmail.com + +priority: optional + +section: x11 + +installed_size: 75700 + +essential: false + +icon: assets/icon/logo.png + +description: + short: Beautiful, Fast and Functional Audiobook Player for your Audiobookshelf server. + long: | + Vaani is a client for your (self-hosted) Audiobookshelf server. + + Features: + - Functional Player: Speed Control, Sleep Timer, Shake to Control Player + - Save data with Offline listening and caching + - Material Design + - Extensive Settings to customize every tiny detail + + Note: you need an Audiobookshelf server setup for this app to work. + Please see https://www.audiobookshelf.org/ on how to setup one if not already. + +postuninstall_scripts: + - echo "Sorry to see you go." + +keywords: + - Audiobook + - Audiobook Player + - Audiobookshelf + +generic_name: Audiobook Player + +categories: + - AudioVideo + - Audio + - Player + +startup_notify: true + +# https://github.com/llfbandit/app_links/blob/051f53fa6039cbfaef0fcde73df20fef9e248cab/doc/README_linux.md +supported_mime_type: + - x-scheme-handler/vaani diff --git a/pubspec.lock b/pubspec.lock index e675e19..03a42ff 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,31 +5,26 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d url: "https://pub.dev" source: hosted - version: "72.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.2" + version: "91.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 url: "https://pub.dev" source: hosted - version: "6.7.0" - analyzer_plugin: + version: "8.4.1" + analyzer_buffer: dependency: transitive description: - name: analyzer_plugin - sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + name: analyzer_buffer + sha256: aba2f75e63b3135fd1efaa8b6abefe1aa6e41b6bd9806221620fa48f98156033 url: "https://pub.dev" source: hosted - version: "0.11.3" + version: "0.1.11" animated_list_plus: dependency: "direct main" description: @@ -47,61 +42,61 @@ packages: source: hosted version: "2.0.10" archive: - dependency: transitive + dependency: "direct main" description: name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "4.0.7" args: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.7.0" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.13.0" audio_service: dependency: "direct main" description: name: audio_service - sha256: "9dd5ba7e77567b290c35908b1950d61485b4dfdd3a0ac398e98cfeec04651b75" + sha256: cb122c7c2639d2a992421ef96b67948ad88c5221da3365ccef1031393a76e044 url: "https://pub.dev" source: hosted - version: "0.18.15" + version: "0.18.18" audio_service_platform_interface: dependency: transitive description: name: audio_service_platform_interface - sha256: "8431a455dac9916cc9ee6f7da5620a666436345c906ad2ebb7fa41d18b3c1bf4" + sha256: "6283782851f6c8b501b60904a32fc7199dc631172da0629d7301e66f672ab777" url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.1.3" audio_service_web: dependency: transitive description: name: audio_service_web - sha256: "4cdc2127cd4562b957fb49227dc58e3303fafb09bde2573bc8241b938cf759d9" + sha256: b8ea9243201ee53383157fbccf13d5d2a866b5dda922ec19d866d1d5d70424df url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.1.4" audio_session: dependency: "direct main" description: name: audio_session - sha256: "343e83bc7809fbda2591a49e525d6b63213ade10c76f15813be9aed6657b3261" + sha256: "2b7fff16a552486d078bfc09a8cde19f426dc6d6329262b684182597bec5b1ac" url: "https://pub.dev" source: hosted - version: "0.1.21" + version: "0.1.25" audio_video_progress_bar: dependency: "direct main" description: @@ -122,66 +117,50 @@ packages: dependency: "direct main" description: name: background_downloader - sha256: "6b73fa5d20c47e855f6ef3ed6fb3e0d164141d8ae7d43ca0a42c78f90eaa15e7" + sha256: d3016a9eb584f6cb16384c8b4a008943c39119730d60046044349b5dbbda4ccb url: "https://pub.dev" source: hosted - version: "8.5.6" + version: "9.2.2" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" build: dependency: transitive description: name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "4.0.3" build_config: dependency: transitive description: name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" build_daemon: dependency: transitive description: name: build_daemon - sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" + sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" url: "https://pub.dev" source: hosted - version: "4.0.2" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.dev" - source: hosted - version: "2.4.2" + version: "4.0.4" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" + sha256: "110c56ef29b5eb367b4d17fc79375fa8c18a6cd7acd92c05bb3986c17a079057" url: "https://pub.dev" source: hosted - version: "2.4.13" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 - url: "https://pub.dev" - source: hosted - version: "7.3.2" + version: "2.10.4" built_collection: dependency: transitive description: @@ -194,10 +173,10 @@ packages: dependency: transitive description: name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + sha256: "426cf75afdb23aa74bd4e471704de3f9393f3c7b04c1e2d9c6f1073ae0b8b139" url: "https://pub.dev" source: hosted - version: "8.9.2" + version: "8.12.1" cached_network_image: dependency: "direct main" description: @@ -222,14 +201,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + chalkdart: + dependency: transitive + description: + name: chalkdart + sha256: "7ffc6bd39c81453fb9ba8dbce042a9c960219b75ea1c07196a7fa41c2fab9e86" + url: "https://pub.dev" + source: hosted + version: "3.0.5" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -238,30 +225,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" - ci: + cli_config: dependency: transitive description: - name: ci - sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec url: "https://pub.dev" source: hosted - version: "0.1.0" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 - url: "https://pub.dev" - source: hosted - version: "0.4.1" + version: "0.2.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" coast: dependency: "direct main" description: @@ -274,26 +253,34 @@ packages: dependency: transitive description: name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.10.1" collection: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" convert: dependency: transitive description: name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + url: "https://pub.dev" + source: hosted + version: "1.15.0" cross_file: dependency: transitive description: @@ -306,10 +293,10 @@ packages: dependency: transitive description: name: crypto - sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" cupertino_icons: dependency: "direct main" description: @@ -318,54 +305,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" - custom_lint: - dependency: "direct dev" - description: - name: custom_lint - sha256: "6e1ec47427ca968f22bce734d00028ae7084361999b41673291138945c5baca0" - url: "https://pub.dev" - source: hosted - version: "0.6.7" - custom_lint_builder: - dependency: transitive - description: - name: custom_lint_builder - sha256: ba2f90fff4eff71d202d097eb14b14f87087eaaef742e956208c0eb9d3a40a21 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - custom_lint_core: - dependency: transitive - description: - name: custom_lint_core - sha256: "4ddbbdaa774265de44c97054dcec058a83d9081d071785ece601e348c18c267d" - url: "https://pub.dev" - source: hosted - version: "0.6.5" dart_style: dependency: transitive description: name: dart_style - sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b url: "https://pub.dev" source: hosted - version: "2.3.7" + version: "3.1.3" device_info_plus: dependency: "direct main" description: name: device_info_plus - sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074 + sha256: "0c6396126421b590089447154c5f98a5de423b70cfb15b1578fd018843ee6f53" url: "https://pub.dev" source: hosted - version: "10.1.2" + version: "11.4.0" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba" + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.2" + dio: + dependency: transitive + description: + name: dio + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" duration_picker: dependency: "direct main" description: @@ -374,6 +353,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + dynamic_color: + dependency: "direct main" + description: + name: dynamic_color + sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d + url: "https://pub.dev" + source: hosted + version: "1.7.0" easy_stepper: dependency: "direct main" description: @@ -386,42 +373,42 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: name: ffi - sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" file: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" file_picker: - dependency: transitive + dependency: "direct main" description: name: file_picker - sha256: "167bb619cdddaa10ef2907609feb8a79c16dfa479d3afaf960f8e223f754bf12" + sha256: "77f8e81d22d2a07d0dee2c62e1dda71dc1da73bf43bb2d45af09727406167964" url: "https://pub.dev" source: hosted - version: "8.1.2" + version: "10.1.9" fixnum: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -431,10 +418,10 @@ packages: dependency: "direct main" description: name: flutter_animate - sha256: "7c8a6594a9252dad30cc2ef16e33270b6248c4dedc3b3d06c86c4f3f4dc05ae5" + sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" url: "https://pub.dev" source: hosted - version: "4.5.0" + version: "4.5.2" flutter_cache_manager: dependency: "direct main" description: @@ -443,22 +430,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.4.1" - flutter_colorpicker: - dependency: transitive - description: - name: flutter_colorpicker - sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea" - url: "https://pub.dev" - source: hosted - version: "1.1.0" flutter_hooks: dependency: "direct main" description: name: flutter_hooks - sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70 + sha256: b772e710d16d7a20c0740c4f855095026b31c7eb5ba3ab67d2bd52021cd9461d url: "https://pub.dev" source: hosted - version: "0.20.5" + version: "0.21.2" flutter_lints: dependency: "direct dev" description: @@ -467,30 +446,22 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" - flutter_material_pickers: - dependency: "direct main" - description: - name: flutter_material_pickers - sha256: "1f0977df9d3977c6621fff602f6956107cf5ff0df58d3441459e5b2e37256131" - url: "https://pub.dev" - source: hosted - version: "3.7.0" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda" + sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e url: "https://pub.dev" source: hosted - version: "2.0.22" + version: "2.0.28" flutter_riverpod: dependency: transitive description: name: flutter_riverpod - sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d" + sha256: "38ec6c303e2c83ee84512f5fc2a82ae311531021938e63d7137eccc107bf3c02" url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "3.1.0" flutter_settings_ui: dependency: "direct main" description: @@ -521,26 +492,26 @@ packages: dependency: "direct main" description: name: font_awesome_flutter - sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f" + sha256: d3a89184101baec7f4600d58840a764d2ef760fe1c5a20ef9e6b0e9b24a07a3a url: "https://pub.dev" source: hosted - version: "10.7.0" + version: "10.8.0" freezed: dependency: "direct dev" description: name: freezed - sha256: "44c19278dd9d89292cf46e97dc0c1e52ce03275f40a97c5a348e802a924bf40e" + sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77" url: "https://pub.dev" source: hosted - version: "2.5.7" + version: "3.2.3" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "3.1.0" frontend_server_client: dependency: transitive description: @@ -553,18 +524,18 @@ packages: dependency: transitive description: name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" go_router: dependency: "direct main" description: name: go_router - sha256: "5cf5fdcf853b0629deb35891c7af643be900c3dcaed7489009f9e7dbcfe55ab6" + sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3 url: "https://pub.dev" source: hosted - version: "14.2.8" + version: "14.8.1" graphs: dependency: transitive description: @@ -573,62 +544,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" - hive: + hive_plus_secure: dependency: "direct main" description: - name: hive - sha256: "10819524df282842ebae12870e2e0e9ebc3e5c4637bec741ad39b919c589cb20" + name: hive_plus_secure + sha256: "0bf08f07b42bc42137cfb151ee7fbe417c8295db69d13bb316d81abecfb02aed" url: "https://pub.dev" source: hosted - version: "4.0.0-dev.2" + version: "1.1.25" hooks_riverpod: dependency: "direct main" description: name: hooks_riverpod - sha256: "97266a91c994951a06ef0ff3a1c7fb261e52ec7f74e87f0614ea0b7411b859b2" + sha256: b880efcd17757af0aa242e5dceac2fb781a014c22a32435a5daa8f17e9d5d8a9 url: "https://pub.dev" source: hosted - version: "2.5.2" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e - url: "https://pub.dev" - source: hosted - version: "4.2.0" + version: "3.1.0" http: dependency: transitive description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.4.0" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" http_parser: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.2" image: dependency: transitive description: name: image - sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" + sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.5.4" infinite_listview: dependency: transitive description: @@ -641,34 +604,34 @@ packages: dependency: transitive description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.2" io: dependency: transitive description: name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b url: "https://pub.dev" source: hosted - version: "1.0.4" - isar: + version: "1.0.5" + isar_plus: dependency: "direct main" description: - name: isar - sha256: ebf74d87c400bd9f7da14acb31932b50c2407edbbd40930da3a6c2a8143f85a8 + name: isar_plus + sha256: "43d874216d2f1fcec06f209ebbc75e7ca9705076d8c7077ec6ac5ea511dad9dd" url: "https://pub.dev" source: hosted - version: "4.0.0-dev.14" - isar_flutter_libs: + version: "1.2.0" + isar_plus_flutter_libs: dependency: "direct main" description: - name: isar_flutter_libs - sha256: "04a3f4035e213ddb6e78d0132a7c80296a085c2088c2a761b4a42ee5add36983" + name: isar_plus_flutter_libs + sha256: e142590a13b5c9d349555ebd72a25ee34901992a2bb32ba6ccfd414519b48adf url: "https://pub.dev" source: hosted - version: "4.0.0-dev.14" + version: "1.2.0" js: dependency: transitive description: @@ -689,83 +652,83 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b + sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3 url: "https://pub.dev" source: hosted - version: "6.8.0" + version: "6.11.2" just_audio: dependency: "direct main" description: name: just_audio - sha256: d8e8aaf417d33e345299c17f6457f72bd4ba0c549dc34607abb5183a354edc4d + sha256: f978d5b4ccea08f267dae0232ec5405c1b05d3f3cd63f82097ea46c015d5c09e url: "https://pub.dev" source: hosted - version: "0.9.40" + version: "0.9.46" just_audio_background: dependency: "direct main" description: path: just_audio_background ref: media-notification-config - resolved-ref: "79ac48a7d322d5b8db8847b35ed0c8555fa249bc" + resolved-ref: fce45f334f0838cb6f630548efb65fec40ff17b4 url: "https://github.com/Dr-Blank/just_audio" source: git - version: "0.0.1-beta.13" + version: "0.0.1-beta.15" just_audio_media_kit: dependency: "direct main" description: name: just_audio_media_kit - sha256: "7f57d317fafa04cb3e70b924e8f632ffb7eca7a97a369e1e44738ed89fbd5da1" + sha256: f3cf04c3a50339709e87e90b4e841eef4364ab4be2bdbac0c54cc48679f84d23 url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.0" just_audio_platform_interface: dependency: transitive description: name: just_audio_platform_interface - sha256: "0243828cce503c8366cc2090cefb2b3c871aa8ed2f520670d76fd47aa1ab2790" + sha256: "4cd94536af0219fa306205a58e78d67e02b0555283c1c094ee41e402a14a5c4a" url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.5.0" just_audio_web: dependency: transitive description: name: just_audio_web - sha256: "9a98035b8b24b40749507687520ec5ab404e291d2b0937823ff45d92cb18d448" + sha256: "6ba8a2a7e87d57d32f0f7b42856ade3d6a9fbe0f1a11fabae0a4f00bb73f0663" url: "https://pub.dev" source: hosted - version: "0.4.13" + version: "0.4.16" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: name: lints - sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.1" list_wheel_scroll_view_nls: dependency: "direct main" description: @@ -774,40 +737,48 @@ packages: url: "https://pub.dev" source: hosted version: "0.0.3" + logger: + dependency: transitive + description: + name: logger + sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3 + url: "https://pub.dev" + source: hosted + version: "2.6.2" logging: dependency: "direct main" description: name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" + logging_appenders: + dependency: "direct main" + description: + name: logging_appenders + sha256: e329e7472f99416d0edaaf6451fe6c02dec91d34535bd252e284a0b94ab23d79 + url: "https://pub.dev" + source: hosted + version: "1.3.1" lottie: dependency: "direct main" description: name: lottie - sha256: "6a24ade5d3d918c306bb1c21a6b9a04aab0489d51a2582522eea820b4093b62b" + sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950 url: "https://pub.dev" source: hosted - version: "3.1.2" - macros: - dependency: transitive - description: - name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" - url: "https://pub.dev" - source: hosted - version: "0.1.2-main.4" + version: "3.3.1" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: - dependency: transitive + dependency: "direct main" description: name: material_color_utilities sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec @@ -818,26 +789,26 @@ packages: dependency: "direct main" description: name: material_symbols_icons - sha256: "66416c4e30bd363508e12669634fc4f3250b83b69e862de67f4f9c480cf42414" + sha256: "7c50901b39d1ad645ee25d920aed008061e1fd541a897b4ebf2c01d966dbf16b" url: "https://pub.dev" source: hosted - version: "4.2785.1" + version: "4.2815.1" media_kit: dependency: transitive description: name: media_kit - sha256: "1f1deee148533d75129a6f38251ff8388e33ee05fc2d20a6a80e57d6051b7b62" + sha256: "48c10c3785df5d88f0eef970743f8c99b2e5da2b34b9d8f9876e598f62d9e776" url: "https://pub.dev" source: hosted - version: "1.1.11" + version: "1.2.0" media_kit_libs_linux: dependency: "direct main" description: name: media_kit_libs_linux - sha256: e186891c31daa6bedab4d74dcdb4e8adfccc7d786bfed6ad81fe24a3b3010310 + sha256: "2b473399a49ec94452c4d4ae51cfc0f6585074398d74216092bf3d54aac37ecf" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.2.1" media_kit_libs_windows_audio: dependency: "direct main" description: @@ -850,18 +821,18 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.0" mime: dependency: transitive description: name: mime - sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "2.0.0" miniplayer: dependency: "direct main" description: @@ -871,6 +842,22 @@ packages: url: "https://github.com/Dr-Blank/miniplayer.git" source: git version: "1.0.3" + mockito: + dependency: transitive + description: + name: mockito + sha256: dac24d461418d363778d53198d9ac0510b9d073869f078450f195766ec48d05e + url: "https://pub.dev" + source: hosted + version: "5.6.1" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" numberpicker: dependency: "direct main" description: @@ -891,58 +878,58 @@ packages: dependency: transitive description: name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" package_info_plus: dependency: "direct main" description: name: package_info_plus - sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918 + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" url: "https://pub.dev" source: hosted - version: "8.0.2" + version: "8.3.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66 + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.2.0" path: dependency: "direct main" description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_provider: dependency: "direct main" description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 url: "https://pub.dev" source: hosted - version: "2.2.10" + version: "2.2.17" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -967,22 +954,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" + url: "https://pub.dev" + source: hosted + version: "11.4.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc + url: "https://pub.dev" + source: hosted + version: "12.1.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 + url: "https://pub.dev" + source: hosted + version: "9.4.7" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + url: "https://pub.dev" + source: hosted + version: "4.3.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" petitparser: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "6.1.0" platform: dependency: transitive description: name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -999,62 +1034,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + posix: + dependency: transitive + description: + name: posix + sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62 + url: "https://pub.dev" + source: hosted + version: "6.0.2" pub_semver: dependency: transitive description: name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.5.0" riverpod: dependency: transitive description: name: riverpod - sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d + sha256: "16ff608d21e8ea64364f2b7c049c94a02ab81668f78845862b6e88b71dd4935a" url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "3.1.0" riverpod_analyzer_utils: dependency: transitive description: name: riverpod_analyzer_utils - sha256: ac28d7bc678471ec986b42d88e5a0893513382ff7542c7ac9634463b044ac72c + sha256: "947b05d04c52a546a2ac6b19ef2a54b08520ff6bdf9f23d67957a4c8df1c3bc0" url: "https://pub.dev" source: hosted - version: "0.5.4" + version: "1.0.0-dev.8" riverpod_annotation: dependency: "direct main" description: name: riverpod_annotation - sha256: e5e796c0eba4030c704e9dae1b834a6541814963292839dcf9638d53eba84f5c + sha256: cc1474bc2df55ec3c1da1989d139dcef22cd5e2bd78da382e867a69a8eca2e46 url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "4.0.0" riverpod_generator: dependency: "direct dev" description: name: riverpod_generator - sha256: "63311e361ffc578d655dfc31b48dfa4ed3bc76fd06f9be845e9bf97c5c11a429" + sha256: e43b1537229cc8f487f09b0c20d15dba840acbadcf5fc6dad7ad5e8ab75950dc url: "https://pub.dev" source: hosted - version: "2.4.3" - riverpod_lint: - dependency: "direct dev" - description: - name: riverpod_lint - sha256: a35a92f2c2a4b7a5d95671c96c5432b42c20f26bb3e985e83d0b186471b61a85 - url: "https://pub.dev" - source: hosted - version: "2.3.13" + version: "4.0.0+1" rxdart: dependency: transitive description: @@ -1067,10 +1102,10 @@ packages: dependency: transitive description: name: safe_local_storage - sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440 + sha256: e9a21b6fec7a8aa62cc2585ff4c1b127df42f3185adbd2aca66b47abe2e80236 url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "2.0.1" scroll_loop_auto_scroll: dependency: "direct main" description: @@ -1083,34 +1118,66 @@ packages: dependency: "direct main" description: name: sensors_plus - sha256: "90f2d38471ca75625f6569d1044d783e0add43548692fbe6e53b008a38a8313a" + sha256: "905282c917c6bb731c242f928665c2ea15445aa491249dea9d98d7c79dc8fd39" url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.1.1" sensors_plus_platform_interface: dependency: transitive description: name: sensors_plus_platform_interface - sha256: b6cacfe243cbeb16403ba688cb0d7054ad4dccb946dcd1254bebdf345fe4b187 + sha256: "58815d2f5e46c0c41c40fb39375d3f127306f7742efe3b891c0b1c87e2b5cd5d" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da + url: "https://pub.dev" + source: hosted + version: "10.1.4" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + url: "https://pub.dev" + source: hosted + version: "5.0.2" shelf: dependency: transitive description: name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.0.0" shelfsdk: dependency: "direct main" description: @@ -1130,47 +1197,63 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" socket_io_client: dependency: transitive description: name: socket_io_client - sha256: "543842390db2c1d1b02e1ad0e6167db2f8872c7a6669051cb89e2559b11a5aeb" + sha256: c8471c2c6843cf308a5532ff653f2bcdb7fa9ae79d84d1179920578a06624f0d url: "https://pub.dev" source: hosted - version: "3.0.0-beta.4" + version: "3.1.2" socket_io_common: dependency: transitive description: name: socket_io_common - sha256: "392c3613c88ad3ee0e15911db2e7e9dbd675622c2589ce99383630603b097619" + sha256: "162fbaecbf4bf9a9372a62a341b3550b51dcef2f02f3e5830a297fd48203d45b" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.1.1" source_gen: dependency: transitive description: name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "4.1.1" source_helper: dependency: transitive description: name: source_helper - sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" url: "https://pub.dev" source: hosted - version: "1.3.4" + version: "1.3.8" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" sprintf: dependency: transitive description: @@ -1183,26 +1266,50 @@ packages: dependency: transitive description: name: sqflite - sha256: ff5a2436ef8ebdfda748fbfe957f9981524cb5ff11e7bafa8c42771840e8a788 + sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 url: "https://pub.dev" source: hosted - version: "2.3.3+2" + version: "2.4.2" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "2d8e607db72e9cb7748c9c6e739e2c9618320a5517de693d5a24609c4671b1a4" + sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" url: "https://pub.dev" source: hosted - version: "2.5.4+4" + version: "2.5.5" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" state_notifier: dependency: transitive description: @@ -1215,66 +1322,74 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: dependency: transitive description: name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" synchronized: dependency: transitive description: name: synchronized - sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" + sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" url: "https://pub.dev" source: hosted - version: "3.3.0+3" + version: "3.3.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" + test: + dependency: transitive + description: + name: test + sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" + url: "https://pub.dev" + source: hosted + version: "1.26.3" test_api: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.2" - timing: + version: "0.7.7" + test_core: dependency: transitive description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + name: test_core + sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "0.6.12" typed_data: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" universal_platform: dependency: transitive description: @@ -1287,50 +1402,50 @@ packages: dependency: transitive description: name: uri_parser - sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835" + sha256: ff4d2c720aca3f4f7d5445e23b11b2d15ef8af5ddce5164643f38ff962dcb270 url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "3.0.0" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.3.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: e35a698ac302dd68e41f73250bd9517fe3ab5fa4f18fe4647a0872db61bacbab + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" url: "https://pub.dev" source: hosted - version: "6.3.10" + version: "6.3.16" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.3.3" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" url_launcher_platform_interface: dependency: transitive description: @@ -1343,18 +1458,18 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.1" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.4" uuid: dependency: transitive description: @@ -1367,90 +1482,98 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vibration: dependency: "direct main" description: name: vibration - sha256: fe8f90e1827f86a4f722b819799ecac8a24789a39c6d562ea316bcaeb8b1ec61 + sha256: "804ee8f9628f31ee71fbe6137a2bc6206a64e101ec22cd9dd6d3a7dc0272591b" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.1.3" vibration_platform_interface: dependency: transitive description: name: vibration_platform_interface - sha256: "735a5fef0f284de0ad9449a5ed7d36ba017c6f59b5b20ac64418af4a6bd35ee7" + sha256: "03e9deaa4df48a1a6212e281bfee5f610d62e9247929dd2f26f4efd4fa5e225c" url: "https://pub.dev" source: hosted - version: "0.0.1" + version: "0.1.0" vm_service: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "15.0.0" watcher: dependency: transitive description: name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" web: dependency: transitive description: name: web - sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" web_socket: dependency: transitive description: name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" url: "https://pub.dev" source: hosted - version: "0.1.6" + version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" win32: dependency: transitive description: name: win32 - sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" + sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" url: "https://pub.dev" source: hosted - version: "5.5.4" + version: "5.13.0" win32_registry: dependency: transitive description: name: win32_registry - sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" url: "https://pub.dev" source: hosted - version: "1.1.5" + version: "2.1.0" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.1.0" xml: dependency: transitive description: @@ -1463,10 +1586,10 @@ packages: dependency: transitive description: name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" sdks: - dart: ">=3.5.0 <4.0.0" - flutter: ">=3.24.0" + dart: ">=3.10.0 <4.0.0" + flutter: "3.38.6" diff --git a/pubspec.yaml b/pubspec.yaml index fc1bfdb..6475fe3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,12 +16,11 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 0.0.10+1 +version: 0.0.19+10 environment: - sdk: ">=3.3.4 <4.0.0" - -isar_version: &isar_version ^4.0.0-dev.13 # define the version to be used + sdk: ">=3.10.0 <4.0.0" + flutter: 3.38.6 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -32,32 +31,34 @@ isar_version: &isar_version ^4.0.0-dev.13 # define the version to be used dependencies: animated_list_plus: ^0.5.2 animated_theme_switcher: ^2.0.10 + archive: ^4.0.5 audio_service: ^0.18.15 - audio_session: ^0.1.19 + audio_session: ^0.1.23 audio_video_progress_bar: ^2.0.2 auto_scroll_text: ^0.0.7 - background_downloader: ^8.5.2 + background_downloader: ^9.2.0 cached_network_image: ^3.3.1 coast: ^2.0.2 collection: ^1.18.0 cupertino_icons: ^1.0.6 - device_info_plus: ^10.1.0 + device_info_plus: ^11.3.3 duration_picker: ^1.2.0 + dynamic_color: ^1.7.0 easy_stepper: ^0.8.4 + file_picker: ^10.0.0 flutter: sdk: flutter flutter_animate: ^4.5.0 flutter_cache_manager: ^3.3.2 - flutter_hooks: ^0.20.5 - flutter_material_pickers: ^3.6.0 + flutter_hooks: ^0.21.2 flutter_settings_ui: ^3.0.1 font_awesome_flutter: ^10.7.0 - freezed_annotation: ^2.4.1 + freezed_annotation: ^3.1.0 go_router: ^14.0.2 - hive: ^4.0.0-dev.2 - hooks_riverpod: ^2.5.1 - isar: ^4.0.0-dev.13 - isar_flutter_libs: ^4.0.0-dev.13 + hive_plus_secure: ^1.1.25 + hooks_riverpod: ^3.0.0 + isar_plus: ^1.1.0 + isar_plus_flutter_libs: ^1.1.0 json_annotation: ^4.9.0 just_audio: ^0.9.37 just_audio_background: @@ -69,7 +70,9 @@ dependencies: just_audio_media_kit: ^2.0.4 list_wheel_scroll_view_nls: ^0.0.3 logging: ^1.2.0 + logging_appenders: ^1.3.1 lottie: ^3.1.0 + material_color_utilities: ^0.11.1 material_symbols_icons: ^4.2785.1 media_kit_libs_linux: any media_kit_libs_windows_audio: any @@ -81,24 +84,25 @@ dependencies: package_info_plus: ^8.0.0 path: ^1.9.0 path_provider: ^2.1.0 - riverpod_annotation: ^2.3.5 + permission_handler: ^11.3.1 + riverpod_annotation: 4.0.0 scroll_loop_auto_scroll: ^0.0.5 sensors_plus: ^6.0.1 + share_plus: ^10.0.2 shelfsdk: path: ./shelfsdk shimmer: ^3.0.0 url_launcher: ^6.2.6 - vibration: ^2.0.0 + vibration: ^3.1.3 dev_dependencies: build_runner: ^2.4.9 - custom_lint: ^0.6.4 + # custom_lint: ^0.8.1 flutter_lints: ^5.0.0 flutter_test: sdk: flutter - freezed: ^2.5.2 + freezed: ^3.1.0 json_serializable: ^6.8.0 - riverpod_generator: ^2.4.2 - riverpod_lint: ^2.3.10 + riverpod_generator: ^4.0.0+1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -114,6 +118,8 @@ flutter: - assets/ - assets/animations/ - assets/sounds/ + - assets/images/ + - assets/fonts/ # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see @@ -139,3 +145,7 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages + fonts: + - family: AbsIcons + fonts: + - asset: assets/fonts/AbsIcons.ttf diff --git a/shelfsdk b/shelfsdk new file mode 160000 index 0000000..34b2b98 --- /dev/null +++ b/shelfsdk @@ -0,0 +1 @@ +Subproject commit 34b2b98a90e3851f50f9657b1ff5bae5f385dbf9 diff --git a/test/features/logging/providers/logs_provider_test.dart b/test/features/logging/providers/logs_provider_test.dart new file mode 100644 index 0000000..68590b8 --- /dev/null +++ b/test/features/logging/providers/logs_provider_test.dart @@ -0,0 +1,24 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:logging/logging.dart'; +import 'package:logging_appenders/logging_appenders.dart'; +import 'package:vaani/features/logging/providers/logs_provider.dart'; + +void main() { + test( + 'Should parse log line', + () async { + final formatter = DefaultLogRecordFormatter(); + final logRecord = LogRecord( + Level.INFO, + 'getting location for name: "logs"', + 'GoRouter', + ); + final expected = parseLogLine( + formatter.format(logRecord), + ); + expect(logRecord.message, expected.message); + expect(logRecord.level, expected.level); + expect(logRecord.loggerName, expected.loggerName); + }, + ); +} diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 1e47bc9..050981d 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,15 +6,24 @@ #include "generated_plugin_registrant.h" -#include +#include +#include #include +#include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { - IsarFlutterLibsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin")); + DynamicColorPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); + IsarPlusFlutterLibsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("IsarPlusFlutterLibsPlugin")); MediaKitLibsWindowsAudioPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("MediaKitLibsWindowsAudioPluginCApi")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index c41e9ee..5b8e406 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,8 +3,11 @@ # list(APPEND FLUTTER_PLUGIN_LIST - isar_flutter_libs + dynamic_color + isar_plus_flutter_libs media_kit_libs_windows_audio + permission_handler_windows + share_plus url_launcher_windows )