Compare commits
No commits in common. "main" and "v0.0.10" have entirely different histories.
3
.fvmrc
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"flutter": "3.38.6"
|
|
||||||
}
|
|
||||||
46
.github/actions/flutter-setup/action.yaml
vendored
|
|
@ -1,46 +0,0 @@
|
||||||
# .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
|
|
||||||
8
.github/release-drafter.yml
vendored
|
|
@ -40,7 +40,7 @@ template: |
|
||||||
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
|
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
|
||||||
|
|
||||||
exclude-labels:
|
exclude-labels:
|
||||||
- "skip changelog"
|
- "skip-changelog"
|
||||||
|
|
||||||
exclude-contributors:
|
exclude-contributors:
|
||||||
- "Dr-Blank"
|
- "Dr-Blank"
|
||||||
|
|
@ -55,15 +55,15 @@ autolabeler:
|
||||||
branch:
|
branch:
|
||||||
- '/feature\/.+/'
|
- '/feature\/.+/'
|
||||||
title:
|
title:
|
||||||
- "/^feat(ure)?/i"
|
- "/feat(ure)?/i"
|
||||||
body:
|
body:
|
||||||
- "/JIRA-[0-9]{1,4}/"
|
- "/JIRA-[0-9]{1,4}/"
|
||||||
- label: "chore"
|
- label: "chore"
|
||||||
title:
|
title:
|
||||||
- "/^chore\b/i"
|
- "/chore/i"
|
||||||
- label: "ui"
|
- label: "ui"
|
||||||
title:
|
title:
|
||||||
- "/^ui\b/i"
|
- "/^ui\b/i"
|
||||||
- label: "refactor"
|
- label: "refactor"
|
||||||
title:
|
title:
|
||||||
- "/^refactor/i"
|
- "/refactor/i"
|
||||||
|
|
|
||||||
218
.github/workflows/flutter-ci.yaml
vendored
|
|
@ -1,218 +0,0 @@
|
||||||
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
|
|
||||||
84
.github/workflows/flutter_release.yaml
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
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 }}
|
||||||
53
.github/workflows/flutter_test.yaml
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
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
|
||||||
130
.github/workflows/prepare-release.yaml
vendored
|
|
@ -1,130 +0,0 @@
|
||||||
# .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 }}"
|
|
||||||
9
.gitignore
vendored
|
|
@ -30,7 +30,6 @@ migrate_working_dir/
|
||||||
.pub-cache/
|
.pub-cache/
|
||||||
.pub/
|
.pub/
|
||||||
/build/
|
/build/
|
||||||
dist/
|
|
||||||
|
|
||||||
# Symbolication related
|
# Symbolication related
|
||||||
app.*.symbols
|
app.*.symbols
|
||||||
|
|
@ -42,10 +41,6 @@ app.*.map.json
|
||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
/android/app/.cxx/
|
|
||||||
|
|
||||||
# secret keys
|
# separate git repo for api sdk
|
||||||
/secrets
|
/shelfsdk
|
||||||
|
|
||||||
# FVM Version Cache
|
|
||||||
.fvm/
|
|
||||||
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "shelfsdk"]
|
|
||||||
path = shelfsdk
|
|
||||||
url = https://github.com/Dr-Blank/shelfsdk
|
|
||||||
1
.vscode/launch.json
vendored
|
|
@ -7,7 +7,6 @@
|
||||||
{
|
{
|
||||||
"name": "vaani",
|
"name": "vaani",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "lib/main.dart",
|
|
||||||
"type": "dart"
|
"type": "dart"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
28
.vscode/settings.json
vendored
|
|
@ -1,35 +1,27 @@
|
||||||
{
|
{
|
||||||
"cmake.configureOnOpen": false,
|
"workbench.colorCustomizations": {
|
||||||
|
"activityBar.background": "#5A1021",
|
||||||
|
"titleBar.activeBackground": "#7E162E",
|
||||||
|
"titleBar.activeForeground": "#FEFBFC"
|
||||||
|
},
|
||||||
|
"files.exclude": {
|
||||||
|
"**/*.freezed.dart": true,
|
||||||
|
"**/*.g.dart": true
|
||||||
|
},
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"audioplayers",
|
"audioplayers",
|
||||||
"autolabeler",
|
"autolabeler",
|
||||||
"Autovalidate",
|
"Autovalidate",
|
||||||
"Checkmark",
|
|
||||||
"Debounceable",
|
|
||||||
"deeplinking",
|
"deeplinking",
|
||||||
"fullscreen",
|
"fullscreen",
|
||||||
"Lerp",
|
"Lerp",
|
||||||
"miniplayer",
|
"miniplayer",
|
||||||
"mocktail",
|
"mocktail",
|
||||||
"nodename",
|
|
||||||
"numberpicker",
|
|
||||||
"riverpod",
|
"riverpod",
|
||||||
"Schyler",
|
|
||||||
"shelfsdk",
|
"shelfsdk",
|
||||||
"sysname",
|
|
||||||
"tapable",
|
"tapable",
|
||||||
"unfocus",
|
"unfocus",
|
||||||
"utsname",
|
|
||||||
"Vaani"
|
"Vaani"
|
||||||
],
|
],
|
||||||
"dart.flutterSdkPath": ".fvm/versions/3.38.6",
|
"cmake.configureOnOpen": false
|
||||||
"files.exclude": {
|
|
||||||
"**/*.freezed.dart": true,
|
|
||||||
"**/*.g.dart": true
|
|
||||||
},
|
|
||||||
"workbench.colorCustomizations": {
|
|
||||||
"activityBar.background": "#5A1021",
|
|
||||||
"titleBar.activeBackground": "#7E162E",
|
|
||||||
"titleBar.activeForeground": "#FEFBFC"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
181
CONTRIBUTING.md
|
|
@ -1,181 +0,0 @@
|
||||||
# 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:
|
|
||||||
`<type>(scope): <description>`
|
|
||||||
|
|
||||||
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! 🌟
|
|
||||||
3
Gemfile
|
|
@ -1,3 +0,0 @@
|
||||||
source "https://rubygems.org"
|
|
||||||
|
|
||||||
gem "fastlane"
|
|
||||||
221
Gemfile.lock
|
|
@ -1,221 +0,0 @@
|
||||||
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
|
|
||||||
16
README.md
|
|
@ -18,24 +18,18 @@ Client for [Audiobookshelf](https://github.com/advplyr/audiobookshelf) server ma
|
||||||
### Android
|
### Android
|
||||||
|
|
||||||
<!-- a github image with link to releases for download -->
|
<!-- a github image with link to releases for download -->
|
||||||
[<img src="https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/badge_obtainium.png" alt="Get it on Obtainium" height="80">](http://apps.obtainium.imranr.dev/redirect.html?r=obtainium://add/https://github.com/Dr-Blank/Vaani)
|
[<img src="https://github.com/NeoApplications/Neo-Backup/raw/main/badge_github.png" alt="Get it on GitHub" height="80">](https://github.com/Dr-Blank/Vaani/releases/latest/download/app-release-universal.apk) [<img src="https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/badge_obtainium.png" alt="Get it on Obtainium" height="80">](http://apps.obtainium.imranr.dev/redirect.html?r=obtainium://add/https://github.com/Dr-Blank/Vaani)
|
||||||
[<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" alt="Get it on Google Play" height="80">](https://play.google.com/store/apps/details?id=dr.blank.vaani)
|
|
||||||
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyOnDroid" height="80">](https://apt.izzysoft.de/fdroid/index/apk/dr.blank.vaani)
|
|
||||||
[<img src="https://github.com/NeoApplications/Neo-Backup/raw/main/badge_github.png" alt="Get it on GitHub" height="80">](https://github.com/Dr-Blank/Vaani/releases/latest/download/app-universal-release.apk)
|
|
||||||
|
|
||||||
*<small>Play Store version is paid if you want to support the development.</small>*
|
Playstore App is in closed testing. To join testing
|
||||||
|
1. [Join the Google Group](https://groups.google.com/g/vaani-app)
|
||||||
### Linux
|
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)
|
||||||
|
|
||||||
[<img src="https://img.shields.io/badge/.deb-Download-blue" alt="Download Linux (.deb)" height="30">](https://github.com/Dr-Blank/Vaani/releases/latest/download/vaani-linux-amd64.deb)
|
|
||||||
[<img src="https://img.shields.io/badge/AppImage-Download-blue" alt="Download Linux (AppImage)" height="30">](https://github.com/Dr-Blank/Vaani/releases/latest/download/vaani-linux-amd64.AppImage)
|
|
||||||
|
|
||||||
## Screencaps
|
## Screencaps
|
||||||
|
|
||||||
https://github.com/user-attachments/assets/2ac9ace2-4a3c-40fc-adde-55914e4cf62d
|
https://github.com/user-attachments/assets/2ac9ace2-4a3c-40fc-adde-55914e4cf62d
|
||||||
|
|
||||||
|<img src="images/screenshots/android/home.jpg" width="200" />|<img src="images/screenshots/android/bookview.jpg" width="200" />|<img src="images/screenshots/android/player.jpg" width="200" />|
|
|<img src="images/screenshots/android/home.jpg" width="200" />|<img src="images/screenshots/android/bookview.jpg" width="200" />|<img src="images/screenshots/android/player.jpg" width="200" />|
|
||||||
| :-----------------------------------------------------------: | :---------------------------------------------------------------: | :-------------------------------------------------------------: |
|
|:---:|:---:|:---:|
|
||||||
|Home|Book View|Player|
|
|Home|Book View|Player|
|
||||||
|
|
||||||
Currently, the app is in development and is not ready for production use.
|
Currently, the app is in development and is not ready for production use.
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,6 @@ linter:
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
require_trailing_commas: true
|
require_trailing_commas: true
|
||||||
analyzer:
|
analyzer:
|
||||||
exclude:
|
|
||||||
- '**.freezed.dart'
|
|
||||||
- '**.g.dart'
|
|
||||||
- '**.gr.dart'
|
|
||||||
errors:
|
errors:
|
||||||
invalid_annotation_target: ignore
|
invalid_annotation_target: ignore
|
||||||
plugins:
|
plugins:
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,6 @@ android {
|
||||||
namespace "dr.blank.vaani"
|
namespace "dr.blank.vaani"
|
||||||
compileSdk flutter.compileSdkVersion
|
compileSdk flutter.compileSdkVersion
|
||||||
ndkVersion flutter.ndkVersion
|
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 {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
|
@ -50,21 +46,12 @@ android {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
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 {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "dr.blank.vaani"
|
applicationId "dr.blank.vaani"
|
||||||
// You can update the following values to match your application needs.
|
// 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.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion 23
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
|
@ -93,11 +80,3 @@ flutter {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {}
|
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,9 @@
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<application
|
<application
|
||||||
android:label="Vaani"
|
android:label="Vaani"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:usesCleartextTraffic="true"
|
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<!-- android:name=".MainActivity" -->
|
<!-- android:name=".MainActivity" -->
|
||||||
<activity
|
<activity
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
|
||||||
networkTimeout=10000
|
|
||||||
validateDistributionUrl=true
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ pluginManagement {
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
id "com.android.application" version '8.10.0' apply false
|
id "com.android.application" version "7.3.0" apply false
|
||||||
id "org.jetbrains.kotlin.android" version "2.1.10" apply false
|
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include ":app"
|
include ":app"
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 3.8 KiB |
|
|
@ -1,8 +0,0 @@
|
||||||
output: dist/
|
|
||||||
releases:
|
|
||||||
- name: dev
|
|
||||||
jobs:
|
|
||||||
- name: release-dev-linux-deb
|
|
||||||
package:
|
|
||||||
platform: linux
|
|
||||||
target: deb
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
this is how i converted my png to svg
|
|
||||||
|
|
||||||
`convert -background White vaani_logo.png vaani_logo.pbm`
|
|
||||||
|
|
||||||
|
|
||||||
`potrace -b svg -i vaani_logo.pbm -o vaani_logo.svg`
|
|
||||||
|
|
||||||
`-i` flag was needed so that it took white as the svgs and black as background
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
# Linux Build Guide
|
|
||||||
|
|
||||||
## Determining Package Size
|
|
||||||
To determine the installed size for your Linux package configuration, you can use the following script:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Build the Linux app
|
|
||||||
flutter build linux
|
|
||||||
|
|
||||||
# Get size in KB and add 17% buffer for runtime dependencies
|
|
||||||
SIZE_KB=$(du -sk build/linux/x64/release/bundle | cut -f1)
|
|
||||||
BUFFER_SIZE_KB=$(($SIZE_KB + ($SIZE_KB * 17 / 100)))
|
|
||||||
|
|
||||||
echo "Actual bundle size: $SIZE_KB KB"
|
|
||||||
echo "Recommended installed_size (with 17% buffer): $BUFFER_SIZE_KB KB"
|
|
||||||
```
|
|
||||||
|
|
||||||
Save this as `get_package_size.sh` in your project root and make it executable:
|
|
||||||
```bash
|
|
||||||
chmod +x get_package_size.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
1. Run the script:
|
|
||||||
```bash
|
|
||||||
./get_package_size.sh
|
|
||||||
```
|
|
||||||
2. Use the output value for `installed_size` in your `linux/packaging/deb/make_config.yaml` file:
|
|
||||||
```yaml
|
|
||||||
installed_size: 75700 # Replace with the value from the script
|
|
||||||
```
|
|
||||||
|
|
||||||
### Why add a buffer?
|
|
||||||
The 17% buffer is added to account for:
|
|
||||||
- Runtime dependencies
|
|
||||||
- Future updates
|
|
||||||
- Potential additional assets
|
|
||||||
- Prevent installation issues on systems with limited space
|
|
||||||
|
|
||||||
### Notes
|
|
||||||
- The installed size should be specified in kilobytes (KB)
|
|
||||||
- Always round up the buffer size to be safe
|
|
||||||
- Re-run this script after significant changes to your app (new assets, dependencies, etc.)
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
to test deeplink
|
|
||||||
`xdg-open vaani://test?code=123&state=abc`
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
json_key_file("./secrets/play-store-credentials.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
|
|
||||||
package_name("dr.blank.vaani") # e.g. com.krausefx.app
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
# This file contains the fastlane.tools configuration
|
|
||||||
# You can find the documentation at https://docs.fastlane.tools
|
|
||||||
#
|
|
||||||
# For a list of all available actions, check out
|
|
||||||
#
|
|
||||||
# https://docs.fastlane.tools/actions
|
|
||||||
#
|
|
||||||
# For a list of all available plugins, check out
|
|
||||||
#
|
|
||||||
# https://docs.fastlane.tools/plugins/available-plugins
|
|
||||||
#
|
|
||||||
|
|
||||||
# Uncomment the line if you want fastlane to automatically update itself
|
|
||||||
# update_fastlane
|
|
||||||
|
|
||||||
default_platform(:android)
|
|
||||||
|
|
||||||
platform :android do
|
|
||||||
desc "Runs all the tests"
|
|
||||||
lane :test do
|
|
||||||
gradle(task: "test", project_dir: 'android/')
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "Submit a new Beta Build to Crashlytics Beta"
|
|
||||||
lane :beta do
|
|
||||||
gradle(task: "clean assembleRelease", project_dir: 'android/')
|
|
||||||
crashlytics
|
|
||||||
|
|
||||||
# sh "your_script.sh"
|
|
||||||
# You can also use other beta testing services here
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "Deploy a new version to the Google Play"
|
|
||||||
lane :deploy do
|
|
||||||
gradle(task: "clean assembleRelease", project_dir: 'android/')
|
|
||||||
upload_to_play_store
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
fastlane documentation
|
|
||||||
----
|
|
||||||
|
|
||||||
# Installation
|
|
||||||
|
|
||||||
Make sure you have the latest version of the Xcode command line tools installed:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
xcode-select --install
|
|
||||||
```
|
|
||||||
|
|
||||||
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
|
||||||
|
|
||||||
# Available Actions
|
|
||||||
|
|
||||||
## Android
|
|
||||||
|
|
||||||
### android test
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[bundle exec] fastlane android test
|
|
||||||
```
|
|
||||||
|
|
||||||
Runs all the tests
|
|
||||||
|
|
||||||
### android beta
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[bundle exec] fastlane android beta
|
|
||||||
```
|
|
||||||
|
|
||||||
Submit a new Beta Build to Crashlytics Beta
|
|
||||||
|
|
||||||
### android deploy
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[bundle exec] fastlane android deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
Deploy a new version to the Google Play
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
|
||||||
|
|
||||||
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
|
|
||||||
|
|
||||||
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<i>Vaani</i> is a client for your (self-hosted) <a href='https://github.com/advplyr/audiobookshelf'>Audiobookshelf</a> server.
|
|
||||||
|
|
||||||
<b>Features:</b>
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 296 KiB |
|
Before Width: | Height: | Size: 746 KiB |
|
Before Width: | Height: | Size: 304 KiB |
|
Before Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 345 KiB |
|
|
@ -1 +0,0 @@
|
||||||
Beautiful, Fast and Functional Audiobook Player for your Audiobookshelf server.
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Vaani
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<testsuites>
|
|
||||||
<testsuite name="fastlane.lanes">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.0039338">
|
|
||||||
|
|
||||||
</testcase>
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: test" time="10.3552991">
|
|
||||||
|
|
||||||
</testcase>
|
|
||||||
|
|
||||||
</testsuite>
|
|
||||||
</testsuites>
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
|
||||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="192.000000pt" height="192.000000pt"
|
|
||||||
viewBox="0 0 192.000000 192.000000" preserveAspectRatio="xMidYMid meet">
|
|
||||||
<metadata>
|
|
||||||
Created by potrace 1.16, written by Peter Selinger 2001-2019
|
|
||||||
</metadata>
|
|
||||||
<g transform="translate(0.000000,192.000000) scale(0.100000,-0.100000)" fill="#ffffff" stroke="none">
|
|
||||||
<path d="M602 1678 c-18 -18 -17 -689 1 -741 17 -48 30 -61 55 -53 25 8 38 57
|
|
||||||
23 85 -6 11 -11 75 -11 144 0 284 -13 548 -27 563 -18 17 -25 18 -41 2z" />
|
|
||||||
<path d="M897 1683 c-4 -3 -7 -176 -7 -383 l0 -377 23 -22 c30 -28 53 -19 67
|
|
||||||
25 10 29 9 39 -4 59 -14 21 -16 69 -16 353 0 242 -3 331 -12 340 -13 13 -41
|
|
||||||
16 -51 5z" />
|
|
||||||
<path d="M1329 1668 c-8 -15 -10 -104 -7 -311 4 -288 4 -289 27 -308 16 -13
|
|
||||||
28 -16 39 -10 13 8 15 46 13 312 -1 169 -7 311 -12 321 -13 25 -46 23 -60 -4z" />
|
|
||||||
<path d="M1035 1568 c-3 -7 -6 -123 -8 -258 l-2 -245 27 -3 c36 -4 36 -5 43
|
|
||||||
266 7 234 4 252 -37 252 -10 0 -21 -6 -23 -12z" />
|
|
||||||
<path d="M1474 1467 c-2 -7 -3 -60 -2 -118 3 -100 4 -104 26 -107 42 -6 49 10
|
|
||||||
45 105 -5 123 -8 133 -39 133 -13 0 -27 -6 -30 -13z" />
|
|
||||||
<path d="M443 1408 c-13 -23 -3 -251 13 -275 28 -45 88 -19 64 27 -6 10 -10
|
|
||||||
64 -10 120 0 56 -5 110 -10 121 -12 21 -45 25 -57 7z" />
|
|
||||||
<path d="M777 1407 c-12 -9 -16 -39 -19 -132 l-3 -120 30 0 c37 0 43 18 44
|
|
||||||
147 1 85 -1 99 -18 107 -13 7 -23 7 -34 -2z" />
|
|
||||||
<path d="M1182 1398 c-8 -8 -12 -48 -12 -115 0 -93 2 -103 21 -113 39 -21 49
|
|
||||||
2 49 111 0 75 -4 101 -16 113 -19 19 -26 20 -42 4z" />
|
|
||||||
<path d="M1505 1048 c-11 -6 -25 -14 -31 -17 -56 -30 -192 -131 -223 -164 -57
|
|
||||||
-62 -134 -166 -158 -214 -12 -23 -26 -50 -32 -60 -9 -17 -30 -66 -60 -138
|
|
||||||
l-12 -29 -21 34 c-114 189 -151 238 -243 316 -44 38 -107 95 -141 127 -33 31
|
|
||||||
-66 57 -73 57 -8 0 -30 8 -50 19 -20 10 -55 27 -79 37 -39 16 -44 16 -58 2
|
|
||||||
-29 -29 -8 -52 84 -93 51 -23 106 -57 132 -81 25 -23 65 -58 90 -78 59 -49
|
|
||||||
152 -147 186 -199 15 -23 35 -53 44 -67 10 -14 35 -63 56 -108 28 -61 45 -84
|
|
||||||
61 -88 28 -7 49 8 57 41 8 33 106 246 128 279 7 11 27 40 43 66 51 79 135 167
|
|
||||||
192 202 30 18 67 42 81 53 15 11 44 29 65 39 31 16 37 24 35 45 -3 29 -38 38
|
|
||||||
-73 19z" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB |
|
|
@ -2,22 +2,20 @@
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:shelfsdk/audiobookshelf_api.dart';
|
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||||
import 'package:vaani/db/cache_manager.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/api_settings_provider.dart';
|
||||||
import 'package:vaani/settings/models/authenticated_user.dart';
|
|
||||||
import 'package:vaani/shared/extensions/obfuscation.dart';
|
|
||||||
|
|
||||||
part 'api_provider.g.dart';
|
part 'api_provider.g.dart';
|
||||||
|
|
||||||
// TODO: workaround for https://github.com/rrousselGit/riverpod/issues/3718
|
// TODO: workaround for https://github.com/rrousselGit/riverpod/issues/3718
|
||||||
typedef ResponseErrorHandler =
|
typedef ResponseErrorHandler = void Function(
|
||||||
void Function(Response response, [Object? error]);
|
Response response, [
|
||||||
|
Object? error,
|
||||||
|
]);
|
||||||
|
|
||||||
final _logger = Logger('api_provider');
|
final _logger = Logger('api_provider');
|
||||||
|
|
||||||
|
|
@ -33,21 +31,23 @@ Uri makeBaseUrl(String address) {
|
||||||
|
|
||||||
/// get the api instance for the given base url
|
/// get the api instance for the given base url
|
||||||
@riverpod
|
@riverpod
|
||||||
AudiobookshelfApi audiobookshelfApi(Ref ref, Uri? baseUrl) {
|
AudiobookshelfApi audiobookshelfApi(AudiobookshelfApiRef ref, Uri? baseUrl) {
|
||||||
// try to get the base url from app settings
|
// try to get the base url from app settings
|
||||||
final apiSettings = ref.watch(apiSettingsProvider);
|
final apiSettings = ref.watch(apiSettingsProvider);
|
||||||
baseUrl ??= apiSettings.activeServer?.serverUrl;
|
baseUrl ??= apiSettings.activeServer?.serverUrl;
|
||||||
return AudiobookshelfApi(baseUrl: makeBaseUrl(baseUrl.toString()));
|
return AudiobookshelfApi(
|
||||||
|
baseUrl: makeBaseUrl(baseUrl.toString()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get the api instance for the authenticated user
|
/// get the api instance for the authenticated user
|
||||||
///
|
///
|
||||||
/// if the user is not authenticated throw an error
|
/// if the user is not authenticated throw an error
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
AudiobookshelfApi authenticatedApi(Ref ref) {
|
AudiobookshelfApi authenticatedApi(AuthenticatedApiRef ref) {
|
||||||
final user = ref.watch(apiSettingsProvider.select((s) => s.activeUser));
|
final apiSettings = ref.watch(apiSettingsProvider);
|
||||||
|
final user = apiSettings.activeUser;
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
_logger.severe('No active user can not provide authenticated api');
|
|
||||||
throw StateError('No active user');
|
throw StateError('No active user');
|
||||||
}
|
}
|
||||||
return AudiobookshelfApi(
|
return AudiobookshelfApi(
|
||||||
|
|
@ -58,15 +58,15 @@ AudiobookshelfApi authenticatedApi(Ref ref) {
|
||||||
|
|
||||||
/// ping the server to check if it is reachable
|
/// ping the server to check if it is reachable
|
||||||
@riverpod
|
@riverpod
|
||||||
FutureOr<bool> isServerAlive(Ref ref, String address) async {
|
FutureOr<bool> isServerAlive(IsServerAliveRef ref, String address) async {
|
||||||
if (address.isEmpty) {
|
if (address.isEmpty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await AudiobookshelfApi(
|
return await AudiobookshelfApi(baseUrl: makeBaseUrl(address))
|
||||||
baseUrl: makeBaseUrl(address),
|
.server
|
||||||
).server.ping() ??
|
.ping() ??
|
||||||
false;
|
false;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -76,15 +76,14 @@ FutureOr<bool> isServerAlive(Ref ref, String address) async {
|
||||||
/// fetch status of server
|
/// fetch status of server
|
||||||
@riverpod
|
@riverpod
|
||||||
FutureOr<ServerStatusResponse?> serverStatus(
|
FutureOr<ServerStatusResponse?> serverStatus(
|
||||||
Ref ref,
|
ServerStatusRef ref,
|
||||||
Uri baseUrl, [
|
Uri baseUrl, [
|
||||||
ResponseErrorHandler? responseErrorHandler,
|
ResponseErrorHandler? responseErrorHandler,
|
||||||
]) async {
|
]) async {
|
||||||
_logger.fine('fetching server status: ${baseUrl.obfuscate()}');
|
_logger.fine('fetching server status: $baseUrl');
|
||||||
final api = ref.watch(audiobookshelfApiProvider(baseUrl));
|
final api = ref.watch(audiobookshelfApiProvider(baseUrl));
|
||||||
final res = await api.server.status(
|
final res =
|
||||||
responseErrorHandler: responseErrorHandler,
|
await api.server.status(responseErrorHandler: responseErrorHandler);
|
||||||
);
|
|
||||||
_logger.fine('server status: $res');
|
_logger.fine('server status: $res');
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -97,32 +96,20 @@ class PersonalizedView extends _$PersonalizedView {
|
||||||
final api = ref.watch(authenticatedApiProvider);
|
final api = ref.watch(authenticatedApiProvider);
|
||||||
final apiSettings = ref.watch(apiSettingsProvider);
|
final apiSettings = ref.watch(apiSettingsProvider);
|
||||||
final user = apiSettings.activeUser;
|
final user = apiSettings.activeUser;
|
||||||
if (user == null) {
|
|
||||||
_logger.warning('no active user');
|
|
||||||
yield [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (apiSettings.activeLibraryId == null) {
|
if (apiSettings.activeLibraryId == null) {
|
||||||
// set it to default user library by logging in and getting the library id
|
// set it to default user library by logging in and getting the library id
|
||||||
final login = await ref.read(loginProvider().future);
|
final login =
|
||||||
if (login == null) {
|
await api.login(username: user!.username!, password: user.password!);
|
||||||
_logger.shout('failed to login, not building personalized view');
|
ref.read(apiSettingsProvider.notifier).updateState(
|
||||||
yield [];
|
apiSettings.copyWith(activeLibraryId: login!.userDefaultLibraryId),
|
||||||
return;
|
|
||||||
}
|
|
||||||
ref
|
|
||||||
.read(apiSettingsProvider.notifier)
|
|
||||||
.updateState(
|
|
||||||
apiSettings.copyWith(activeLibraryId: login.userDefaultLibraryId),
|
|
||||||
);
|
);
|
||||||
yield [];
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// try to find in cache
|
// try to find in cache
|
||||||
// final cacheKey = 'personalizedView:${apiSettings.activeLibraryId}';
|
// final cacheKey = 'personalizedView:${apiSettings.activeLibraryId}';
|
||||||
final key = 'personalizedView:${apiSettings.activeLibraryId! + user.id}';
|
var key = 'personalizedView:${apiSettings.activeLibraryId! + user!.id!}';
|
||||||
final cachedRes =
|
final cachedRes = await apiResponseCacheManager.getFileFromMemory(
|
||||||
await apiResponseCacheManager.getFileFromMemory(key) ??
|
key,
|
||||||
|
) ??
|
||||||
await apiResponseCacheManager.getFileFromCache(key);
|
await apiResponseCacheManager.getFileFromCache(key);
|
||||||
if (cachedRes != null) {
|
if (cachedRes != null) {
|
||||||
_logger.fine('reading from cache: $cachedRes for key: $key');
|
_logger.fine('reading from cache: $cachedRes for key: $key');
|
||||||
|
|
@ -139,11 +126,10 @@ class PersonalizedView extends _$PersonalizedView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! exaggerated delay
|
// ! exagerated delay
|
||||||
// await Future.delayed(const Duration(seconds: 2));
|
// await Future.delayed(const Duration(seconds: 2));
|
||||||
final res = await api.libraries.getPersonalized(
|
final res = await api.libraries
|
||||||
libraryId: apiSettings.activeLibraryId!,
|
.getPersonalized(libraryId: apiSettings.activeLibraryId!);
|
||||||
);
|
|
||||||
// debugPrint('personalizedView: ${res!.map((e) => e).toSet()}');
|
// debugPrint('personalizedView: ${res!.map((e) => e).toSet()}');
|
||||||
// save to cache
|
// save to cache
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
|
|
@ -159,19 +145,21 @@ class PersonalizedView extends _$PersonalizedView {
|
||||||
_logger.warning('failed to fetch personalized view');
|
_logger.warning('failed to fetch personalized view');
|
||||||
yield [];
|
yield [];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// method to force refresh the view and ignore the cache
|
// method to force refresh the view and ignore the cache
|
||||||
Future<void> forceRefresh() async {
|
Future<void> forceRefresh() async {
|
||||||
// clear the cache
|
// clear the cache
|
||||||
// TODO: find a better way to clear the cache for only personalized view key
|
|
||||||
return apiResponseCacheManager.emptyCache();
|
return apiResponseCacheManager.emptyCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// fetch continue listening audiobooks
|
/// fetch continue listening audiobooks
|
||||||
@riverpod
|
@riverpod
|
||||||
FutureOr<GetUserSessionsResponse> fetchContinueListening(Ref ref) async {
|
FutureOr<GetUserSessionsResponse> fetchContinueListening(
|
||||||
|
FetchContinueListeningRef ref,
|
||||||
|
) async {
|
||||||
final api = ref.watch(authenticatedApiProvider);
|
final api = ref.watch(authenticatedApiProvider);
|
||||||
final res = await api.me.getSessions();
|
final res = await api.me.getSessions();
|
||||||
// debugPrint(
|
// debugPrint(
|
||||||
|
|
@ -181,46 +169,10 @@ FutureOr<GetUserSessionsResponse> fetchContinueListening(Ref ref) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
FutureOr<User> me(Ref ref) async {
|
FutureOr<User> me(
|
||||||
|
MeRef ref,
|
||||||
|
) async {
|
||||||
final api = ref.watch(authenticatedApiProvider);
|
final api = ref.watch(authenticatedApiProvider);
|
||||||
final errorResponseHandler = ErrorResponseHandler();
|
final res = await api.me.getUser();
|
||||||
final res = await api.me.getUser(
|
return res!;
|
||||||
responseErrorHandler: errorResponseHandler.storeError,
|
|
||||||
);
|
|
||||||
if (res == null) {
|
|
||||||
_logger.severe(
|
|
||||||
'me failed, got response: ${errorResponseHandler.response.obfuscate()}',
|
|
||||||
);
|
|
||||||
throw StateError('me failed');
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
@riverpod
|
|
||||||
FutureOr<LoginResponse?> 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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,536 +6,538 @@ part of 'api_provider.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
String _$audiobookshelfApiHash() => r'2c310ea77fea9918ccf96180a92075acd037bd95';
|
||||||
// ignore_for_file: type=lint, type=warning
|
|
||||||
/// get the api instance for the given base url
|
|
||||||
|
|
||||||
|
/// 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get the api instance for the given base url
|
||||||
|
///
|
||||||
|
/// Copied from [audiobookshelfApi].
|
||||||
@ProviderFor(audiobookshelfApi)
|
@ProviderFor(audiobookshelfApi)
|
||||||
final audiobookshelfApiProvider = AudiobookshelfApiFamily._();
|
const audiobookshelfApiProvider = AudiobookshelfApiFamily();
|
||||||
|
|
||||||
/// get the api instance for the given base url
|
/// get the api instance for the given base url
|
||||||
|
///
|
||||||
final class AudiobookshelfApiProvider
|
/// Copied from [audiobookshelfApi].
|
||||||
extends
|
class AudiobookshelfApiFamily extends Family<AudiobookshelfApi> {
|
||||||
$FunctionalProvider<
|
|
||||||
AudiobookshelfApi,
|
|
||||||
AudiobookshelfApi,
|
|
||||||
AudiobookshelfApi
|
|
||||||
>
|
|
||||||
with $Provider<AudiobookshelfApi> {
|
|
||||||
/// get the api instance for the given base url
|
/// get the api instance for the given base url
|
||||||
AudiobookshelfApiProvider._({
|
///
|
||||||
required AudiobookshelfApiFamily super.from,
|
/// Copied from [audiobookshelfApi].
|
||||||
required Uri? super.argument,
|
const AudiobookshelfApiFamily();
|
||||||
}) : super(
|
|
||||||
retry: null,
|
/// get the api instance for the given base url
|
||||||
|
///
|
||||||
|
/// Copied from [audiobookshelfApi].
|
||||||
|
AudiobookshelfApiProvider call(
|
||||||
|
Uri? baseUrl,
|
||||||
|
) {
|
||||||
|
return AudiobookshelfApiProvider(
|
||||||
|
baseUrl,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AudiobookshelfApiProvider getProviderOverride(
|
||||||
|
covariant AudiobookshelfApiProvider provider,
|
||||||
|
) {
|
||||||
|
return call(
|
||||||
|
provider.baseUrl,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? 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<AudiobookshelfApi> {
|
||||||
|
/// 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',
|
name: r'audiobookshelfApiProvider',
|
||||||
isAutoDispose: true,
|
debugGetCreateSourceHash:
|
||||||
dependencies: null,
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
$allTransitiveDependencies: null,
|
? null
|
||||||
|
: _$audiobookshelfApiHash,
|
||||||
|
dependencies: AudiobookshelfApiFamily._dependencies,
|
||||||
|
allTransitiveDependencies:
|
||||||
|
AudiobookshelfApiFamily._allTransitiveDependencies,
|
||||||
|
baseUrl: baseUrl,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
AudiobookshelfApiProvider._internal(
|
||||||
String debugGetCreateSourceHash() => _$audiobookshelfApiHash();
|
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
|
||||||
String toString() {
|
Override overrideWith(
|
||||||
return r'audiobookshelfApiProvider'
|
AudiobookshelfApi Function(AudiobookshelfApiRef provider) create,
|
||||||
''
|
) {
|
||||||
'($argument)';
|
return ProviderOverride(
|
||||||
}
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
$ProviderElement<AudiobookshelfApi> $createElement(
|
|
||||||
$ProviderPointer pointer,
|
|
||||||
) => $ProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
|
||||||
AudiobookshelfApi create(Ref ref) {
|
|
||||||
final argument = this.argument as Uri?;
|
|
||||||
return audiobookshelfApi(ref, argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// {@macro riverpod.override_with_value}
|
|
||||||
Override overrideWithValue(AudiobookshelfApi value) {
|
|
||||||
return $ProviderOverride(
|
|
||||||
origin: this,
|
origin: this,
|
||||||
providerOverride: $SyncValueProvider<AudiobookshelfApi>(value),
|
override: AudiobookshelfApiProvider._internal(
|
||||||
|
(ref) => create(ref as AudiobookshelfApiRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
baseUrl: baseUrl,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeProviderElement<AudiobookshelfApi> createElement() {
|
||||||
|
return _AudiobookshelfApiProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return other is AudiobookshelfApiProvider && other.argument == argument;
|
return other is AudiobookshelfApiProvider && other.baseUrl == baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return argument.hashCode;
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, baseUrl.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$audiobookshelfApiHash() => r'f23a06c404e11867a7f796877eaca99b8ff25458';
|
mixin AudiobookshelfApiRef on AutoDisposeProviderRef<AudiobookshelfApi> {
|
||||||
|
/// The parameter `baseUrl` of this provider.
|
||||||
|
Uri? get baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/// get the api instance for the given base url
|
class _AudiobookshelfApiProviderElement
|
||||||
|
extends AutoDisposeProviderElement<AudiobookshelfApi>
|
||||||
final class AudiobookshelfApiFamily extends $Family
|
with AudiobookshelfApiRef {
|
||||||
with $FunctionalFamilyOverride<AudiobookshelfApi, Uri?> {
|
_AudiobookshelfApiProviderElement(super.provider);
|
||||||
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
|
@override
|
||||||
String toString() => r'audiobookshelfApiProvider';
|
Uri? get baseUrl => (origin as AudiobookshelfApiProvider).baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _$authenticatedApiHash() => r'f555efb6eede590b5a8d60cad2e6bfc2847e2d14';
|
||||||
|
|
||||||
/// get the api instance for the authenticated user
|
/// get the api instance for the authenticated user
|
||||||
///
|
///
|
||||||
/// if the user is not authenticated throw an error
|
/// if the user is not authenticated throw an error
|
||||||
|
///
|
||||||
|
/// Copied from [authenticatedApi].
|
||||||
@ProviderFor(authenticatedApi)
|
@ProviderFor(authenticatedApi)
|
||||||
final authenticatedApiProvider = AuthenticatedApiProvider._();
|
final authenticatedApiProvider = Provider<AudiobookshelfApi>.internal(
|
||||||
|
authenticatedApi,
|
||||||
/// get the api instance for the authenticated user
|
|
||||||
///
|
|
||||||
/// if the user is not authenticated throw an error
|
|
||||||
|
|
||||||
final class AuthenticatedApiProvider
|
|
||||||
extends
|
|
||||||
$FunctionalProvider<
|
|
||||||
AudiobookshelfApi,
|
|
||||||
AudiobookshelfApi,
|
|
||||||
AudiobookshelfApi
|
|
||||||
>
|
|
||||||
with $Provider<AudiobookshelfApi> {
|
|
||||||
/// get the api instance for the authenticated user
|
|
||||||
///
|
|
||||||
/// if the user is not authenticated throw an error
|
|
||||||
AuthenticatedApiProvider._()
|
|
||||||
: super(
|
|
||||||
from: null,
|
|
||||||
argument: null,
|
|
||||||
retry: null,
|
|
||||||
name: r'authenticatedApiProvider',
|
name: r'authenticatedApiProvider',
|
||||||
isAutoDispose: false,
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$authenticatedApiHash,
|
||||||
dependencies: null,
|
dependencies: null,
|
||||||
$allTransitiveDependencies: null,
|
allTransitiveDependencies: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
typedef AuthenticatedApiRef = ProviderRef<AudiobookshelfApi>;
|
||||||
String debugGetCreateSourceHash() => _$authenticatedApiHash();
|
String _$isServerAliveHash() => r'6ff90b6e0febd2cd4a4d3a5209a59afc778cd3b6';
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
$ProviderElement<AudiobookshelfApi> $createElement(
|
|
||||||
$ProviderPointer pointer,
|
|
||||||
) => $ProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
|
||||||
AudiobookshelfApi create(Ref ref) {
|
|
||||||
return authenticatedApi(ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// {@macro riverpod.override_with_value}
|
|
||||||
Override overrideWithValue(AudiobookshelfApi value) {
|
|
||||||
return $ProviderOverride(
|
|
||||||
origin: this,
|
|
||||||
providerOverride: $SyncValueProvider<AudiobookshelfApi>(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _$authenticatedApiHash() => r'284be2c39823c20fb70035a136c430862c28fa27';
|
|
||||||
|
|
||||||
/// ping the server to check if it is reachable
|
/// ping the server to check if it is reachable
|
||||||
|
///
|
||||||
|
/// Copied from [isServerAlive].
|
||||||
@ProviderFor(isServerAlive)
|
@ProviderFor(isServerAlive)
|
||||||
final isServerAliveProvider = IsServerAliveFamily._();
|
const isServerAliveProvider = IsServerAliveFamily();
|
||||||
|
|
||||||
/// ping the server to check if it is reachable
|
/// ping the server to check if it is reachable
|
||||||
|
///
|
||||||
final class IsServerAliveProvider
|
/// Copied from [isServerAlive].
|
||||||
extends $FunctionalProvider<AsyncValue<bool>, bool, FutureOr<bool>>
|
class IsServerAliveFamily extends Family<AsyncValue<bool>> {
|
||||||
with $FutureModifier<bool>, $FutureProvider<bool> {
|
|
||||||
/// ping the server to check if it is reachable
|
/// ping the server to check if it is reachable
|
||||||
IsServerAliveProvider._({
|
///
|
||||||
required IsServerAliveFamily super.from,
|
/// Copied from [isServerAlive].
|
||||||
required String super.argument,
|
const IsServerAliveFamily();
|
||||||
}) : super(
|
|
||||||
retry: null,
|
/// ping the server to check if it is reachable
|
||||||
name: r'isServerAliveProvider',
|
///
|
||||||
isAutoDispose: true,
|
/// Copied from [isServerAlive].
|
||||||
dependencies: null,
|
IsServerAliveProvider call(
|
||||||
$allTransitiveDependencies: null,
|
String address,
|
||||||
|
) {
|
||||||
|
return IsServerAliveProvider(
|
||||||
|
address,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$isServerAliveHash();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return r'isServerAliveProvider'
|
|
||||||
''
|
|
||||||
'($argument)';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
@override
|
||||||
$FutureProviderElement<bool> $createElement($ProviderPointer pointer) =>
|
IsServerAliveProvider getProviderOverride(
|
||||||
$FutureProviderElement(pointer);
|
covariant IsServerAliveProvider provider,
|
||||||
|
) {
|
||||||
|
return call(
|
||||||
|
provider.address,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> create(Ref ref) {
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
final argument = this.argument as String;
|
|
||||||
return isServerAlive(ref, argument);
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'isServerAliveProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ping the server to check if it is reachable
|
||||||
|
///
|
||||||
|
/// Copied from [isServerAlive].
|
||||||
|
class IsServerAliveProvider extends AutoDisposeFutureProvider<bool> {
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<bool> 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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<bool> createElement() {
|
||||||
|
return _IsServerAliveProviderElement(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return other is IsServerAliveProvider && other.argument == argument;
|
return other is IsServerAliveProvider && other.address == address;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return argument.hashCode;
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, address.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$isServerAliveHash() => r'bb3a53cae1eb64b8760a56864feed47b7a3f1c29';
|
mixin IsServerAliveRef on AutoDisposeFutureProviderRef<bool> {
|
||||||
|
/// The parameter `address` of this provider.
|
||||||
|
String get address;
|
||||||
|
}
|
||||||
|
|
||||||
/// ping the server to check if it is reachable
|
class _IsServerAliveProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<bool> with IsServerAliveRef {
|
||||||
final class IsServerAliveFamily extends $Family
|
_IsServerAliveProviderElement(super.provider);
|
||||||
with $FunctionalFamilyOverride<FutureOr<bool>, 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
|
@override
|
||||||
String toString() => r'isServerAliveProvider';
|
String get address => (origin as IsServerAliveProvider).address;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// fetch status of server
|
String _$serverStatusHash() => r'2739906a1862d09b098588ebd16749a09032ee99';
|
||||||
|
|
||||||
|
/// fetch status of server
|
||||||
|
///
|
||||||
|
/// Copied from [serverStatus].
|
||||||
@ProviderFor(serverStatus)
|
@ProviderFor(serverStatus)
|
||||||
final serverStatusProvider = ServerStatusFamily._();
|
const serverStatusProvider = ServerStatusFamily();
|
||||||
|
|
||||||
/// fetch status of server
|
/// fetch status of server
|
||||||
|
///
|
||||||
final class ServerStatusProvider
|
/// Copied from [serverStatus].
|
||||||
extends
|
class ServerStatusFamily extends Family<AsyncValue<ServerStatusResponse?>> {
|
||||||
$FunctionalProvider<
|
|
||||||
AsyncValue<ServerStatusResponse?>,
|
|
||||||
ServerStatusResponse?,
|
|
||||||
FutureOr<ServerStatusResponse?>
|
|
||||||
>
|
|
||||||
with
|
|
||||||
$FutureModifier<ServerStatusResponse?>,
|
|
||||||
$FutureProvider<ServerStatusResponse?> {
|
|
||||||
/// fetch status of server
|
/// fetch status of server
|
||||||
ServerStatusProvider._({
|
///
|
||||||
required ServerStatusFamily super.from,
|
/// Copied from [serverStatus].
|
||||||
required (Uri, ResponseErrorHandler?) super.argument,
|
const ServerStatusFamily();
|
||||||
}) : 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<ServerStatusResponse?> $createElement(
|
|
||||||
$ProviderPointer pointer,
|
|
||||||
) => $FutureProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
|
||||||
FutureOr<ServerStatusResponse?> 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
|
/// fetch status of server
|
||||||
|
///
|
||||||
final class ServerStatusFamily extends $Family
|
/// Copied from [serverStatus].
|
||||||
with
|
|
||||||
$FunctionalFamilyOverride<
|
|
||||||
FutureOr<ServerStatusResponse?>,
|
|
||||||
(Uri, ResponseErrorHandler?)
|
|
||||||
> {
|
|
||||||
ServerStatusFamily._()
|
|
||||||
: super(
|
|
||||||
retry: null,
|
|
||||||
name: r'serverStatusProvider',
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
isAutoDispose: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// fetch status of server
|
|
||||||
|
|
||||||
ServerStatusProvider call(
|
ServerStatusProvider call(
|
||||||
Uri baseUrl, [
|
Uri baseUrl, [
|
||||||
ResponseErrorHandler? responseErrorHandler,
|
void Function(Response, [Object?])? responseErrorHandler,
|
||||||
]) => ServerStatusProvider._(
|
]) {
|
||||||
argument: (baseUrl, responseErrorHandler),
|
return ServerStatusProvider(
|
||||||
from: this,
|
baseUrl,
|
||||||
|
responseErrorHandler,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => r'serverStatusProvider';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// fetch the personalized view
|
@override
|
||||||
|
ServerStatusProvider getProviderOverride(
|
||||||
|
covariant ServerStatusProvider provider,
|
||||||
|
) {
|
||||||
|
return call(
|
||||||
|
provider.baseUrl,
|
||||||
|
provider.responseErrorHandler,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@ProviderFor(PersonalizedView)
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
final personalizedViewProvider = PersonalizedViewProvider._();
|
|
||||||
|
|
||||||
/// fetch the personalized view
|
@override
|
||||||
final class PersonalizedViewProvider
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
extends $StreamNotifierProvider<PersonalizedView, List<Shelf>> {
|
|
||||||
/// fetch the personalized view
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
PersonalizedViewProvider._()
|
|
||||||
: super(
|
@override
|
||||||
from: null,
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
argument: null,
|
_allTransitiveDependencies;
|
||||||
retry: null,
|
|
||||||
name: r'personalizedViewProvider',
|
@override
|
||||||
isAutoDispose: true,
|
String? get name => r'serverStatusProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// fetch status of server
|
||||||
|
///
|
||||||
|
/// Copied from [serverStatus].
|
||||||
|
class ServerStatusProvider
|
||||||
|
extends AutoDisposeFutureProvider<ServerStatusResponse?> {
|
||||||
|
/// 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,
|
||||||
|
);
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
final Uri baseUrl;
|
||||||
|
final void Function(Response, [Object?])? responseErrorHandler;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<ServerStatusResponse?> Function(ServerStatusRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: ServerStatusProvider._internal(
|
||||||
|
(ref) => create(ref as ServerStatusRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
dependencies: null,
|
dependencies: null,
|
||||||
$allTransitiveDependencies: null,
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
baseUrl: baseUrl,
|
||||||
|
responseErrorHandler: responseErrorHandler,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$personalizedViewHash();
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
PersonalizedView create() => PersonalizedView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$personalizedViewHash() => r'425e89d99d7e4712b4d6a688f3a12442bd66584f';
|
|
||||||
|
|
||||||
/// fetch the personalized view
|
|
||||||
|
|
||||||
abstract class _$PersonalizedView extends $StreamNotifier<List<Shelf>> {
|
|
||||||
Stream<List<Shelf>> build();
|
|
||||||
@$mustCallSuper
|
|
||||||
@override
|
@override
|
||||||
void runBuild() {
|
AutoDisposeFutureProviderElement<ServerStatusResponse?> createElement() {
|
||||||
final ref = this.ref as $Ref<AsyncValue<List<Shelf>>, List<Shelf>>;
|
return _ServerStatusProviderElement(this);
|
||||||
final element =
|
}
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
@override
|
||||||
AnyNotifier<AsyncValue<List<Shelf>>, List<Shelf>>,
|
bool operator ==(Object other) {
|
||||||
AsyncValue<List<Shelf>>,
|
return other is ServerStatusProvider &&
|
||||||
Object?,
|
other.baseUrl == baseUrl &&
|
||||||
Object?
|
other.responseErrorHandler == responseErrorHandler;
|
||||||
>;
|
}
|
||||||
element.handleCreate(ref, build);
|
|
||||||
|
@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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// fetch continue listening audiobooks
|
mixin ServerStatusRef on AutoDisposeFutureProviderRef<ServerStatusResponse?> {
|
||||||
|
/// The parameter `baseUrl` of this provider.
|
||||||
|
Uri get baseUrl;
|
||||||
|
|
||||||
@ProviderFor(fetchContinueListening)
|
/// The parameter `responseErrorHandler` of this provider.
|
||||||
final fetchContinueListeningProvider = FetchContinueListeningProvider._();
|
void Function(Response, [Object?])? get responseErrorHandler;
|
||||||
|
|
||||||
/// fetch continue listening audiobooks
|
|
||||||
|
|
||||||
final class FetchContinueListeningProvider
|
|
||||||
extends
|
|
||||||
$FunctionalProvider<
|
|
||||||
AsyncValue<GetUserSessionsResponse>,
|
|
||||||
GetUserSessionsResponse,
|
|
||||||
FutureOr<GetUserSessionsResponse>
|
|
||||||
>
|
|
||||||
with
|
|
||||||
$FutureModifier<GetUserSessionsResponse>,
|
|
||||||
$FutureProvider<GetUserSessionsResponse> {
|
|
||||||
/// fetch continue listening audiobooks
|
|
||||||
FetchContinueListeningProvider._()
|
|
||||||
: super(
|
|
||||||
from: null,
|
|
||||||
argument: null,
|
|
||||||
retry: null,
|
|
||||||
name: r'fetchContinueListeningProvider',
|
|
||||||
isAutoDispose: true,
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$fetchContinueListeningHash();
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
$FutureProviderElement<GetUserSessionsResponse> $createElement(
|
|
||||||
$ProviderPointer pointer,
|
|
||||||
) => $FutureProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
|
||||||
FutureOr<GetUserSessionsResponse> create(Ref ref) {
|
|
||||||
return fetchContinueListening(ref);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ServerStatusProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<ServerStatusResponse?>
|
||||||
|
with ServerStatusRef {
|
||||||
|
_ServerStatusProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uri get baseUrl => (origin as ServerStatusProvider).baseUrl;
|
||||||
|
@override
|
||||||
|
void Function(Response, [Object?])? get responseErrorHandler =>
|
||||||
|
(origin as ServerStatusProvider).responseErrorHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$fetchContinueListeningHash() =>
|
String _$fetchContinueListeningHash() =>
|
||||||
r'50aeb77369eda38d496b2f56f3df2aea135dab45';
|
r'f65fe3ac3a31b8ac074330525c5d2cc4b526802d';
|
||||||
|
|
||||||
|
/// fetch continue listening audiobooks
|
||||||
|
///
|
||||||
|
/// Copied from [fetchContinueListening].
|
||||||
|
@ProviderFor(fetchContinueListening)
|
||||||
|
final fetchContinueListeningProvider =
|
||||||
|
AutoDisposeFutureProvider<GetUserSessionsResponse>.internal(
|
||||||
|
fetchContinueListening,
|
||||||
|
name: r'fetchContinueListeningProvider',
|
||||||
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$fetchContinueListeningHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef FetchContinueListeningRef
|
||||||
|
= AutoDisposeFutureProviderRef<GetUserSessionsResponse>;
|
||||||
|
String _$meHash() => r'bdc664c4fd867ad13018fa769ce7a6913248c44f';
|
||||||
|
|
||||||
|
/// See also [me].
|
||||||
@ProviderFor(me)
|
@ProviderFor(me)
|
||||||
final meProvider = MeProvider._();
|
final meProvider = AutoDisposeFutureProvider<User>.internal(
|
||||||
|
me,
|
||||||
final class MeProvider
|
|
||||||
extends $FunctionalProvider<AsyncValue<User>, User, FutureOr<User>>
|
|
||||||
with $FutureModifier<User>, $FutureProvider<User> {
|
|
||||||
MeProvider._()
|
|
||||||
: super(
|
|
||||||
from: null,
|
|
||||||
argument: null,
|
|
||||||
retry: null,
|
|
||||||
name: r'meProvider',
|
name: r'meProvider',
|
||||||
isAutoDispose: true,
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product') ? null : _$meHash,
|
||||||
dependencies: null,
|
dependencies: null,
|
||||||
$allTransitiveDependencies: null,
|
allTransitiveDependencies: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
typedef MeRef = AutoDisposeFutureProviderRef<User>;
|
||||||
String debugGetCreateSourceHash() => _$meHash();
|
String _$personalizedViewHash() => r'4c392ece4650bdc36d7195a0ddb8810e8fe4caa9';
|
||||||
|
|
||||||
@$internal
|
/// fetch the personalized view
|
||||||
@override
|
///
|
||||||
$FutureProviderElement<User> $createElement($ProviderPointer pointer) =>
|
/// Copied from [PersonalizedView].
|
||||||
$FutureProviderElement(pointer);
|
@ProviderFor(PersonalizedView)
|
||||||
|
final personalizedViewProvider =
|
||||||
@override
|
AutoDisposeStreamNotifierProvider<PersonalizedView, List<Shelf>>.internal(
|
||||||
FutureOr<User> create(Ref ref) {
|
PersonalizedView.new,
|
||||||
return me(ref);
|
name: r'personalizedViewProvider',
|
||||||
}
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
}
|
? null
|
||||||
|
: _$personalizedViewHash,
|
||||||
String _$meHash() => r'b3b6d6d940b465c60d0c29cd6e81ba2fcccab186';
|
|
||||||
|
|
||||||
@ProviderFor(login)
|
|
||||||
final loginProvider = LoginFamily._();
|
|
||||||
|
|
||||||
final class LoginProvider
|
|
||||||
extends
|
|
||||||
$FunctionalProvider<
|
|
||||||
AsyncValue<LoginResponse?>,
|
|
||||||
LoginResponse?,
|
|
||||||
FutureOr<LoginResponse?>
|
|
||||||
>
|
|
||||||
with $FutureModifier<LoginResponse?>, $FutureProvider<LoginResponse?> {
|
|
||||||
LoginProvider._({
|
|
||||||
required LoginFamily super.from,
|
|
||||||
required AuthenticatedUser? super.argument,
|
|
||||||
}) : super(
|
|
||||||
retry: null,
|
|
||||||
name: r'loginProvider',
|
|
||||||
isAutoDispose: true,
|
|
||||||
dependencies: null,
|
dependencies: null,
|
||||||
$allTransitiveDependencies: null,
|
allTransitiveDependencies: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
typedef _$PersonalizedView = AutoDisposeStreamNotifier<List<Shelf>>;
|
||||||
String debugGetCreateSourceHash() => _$loginHash();
|
// 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 toString() {
|
|
||||||
return r'loginProvider'
|
|
||||||
''
|
|
||||||
'($argument)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
$FutureProviderElement<LoginResponse?> $createElement(
|
|
||||||
$ProviderPointer pointer,
|
|
||||||
) => $FutureProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
|
||||||
FutureOr<LoginResponse?> 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<LoginResponse?>,
|
|
||||||
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';
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,21 @@ import 'package:vaani/api/server_provider.dart'
|
||||||
import 'package:vaani/db/storage.dart';
|
import 'package:vaani/db/storage.dart';
|
||||||
import 'package:vaani/settings/api_settings_provider.dart';
|
import 'package:vaani/settings/api_settings_provider.dart';
|
||||||
import 'package:vaani/settings/models/audiobookshelf_server.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'
|
||||||
import 'package:vaani/shared/extensions/obfuscation.dart';
|
as model;
|
||||||
|
|
||||||
part 'authenticated_users_provider.g.dart';
|
part 'authenticated_user_provider.g.dart';
|
||||||
|
|
||||||
final _box = AvailableHiveBoxes.authenticatedUserBox;
|
final _box = AvailableHiveBoxes.authenticatedUserBox;
|
||||||
|
|
||||||
final _logger = Logger('authenticated_users_provider');
|
final _logger = Logger('authenticated_user_provider');
|
||||||
|
|
||||||
/// provides with a set of authenticated users
|
/// provides with a set of authenticated users
|
||||||
@riverpod
|
@riverpod
|
||||||
class AuthenticatedUsers extends _$AuthenticatedUsers {
|
class AuthenticatedUser extends _$AuthenticatedUser {
|
||||||
@override
|
@override
|
||||||
Set<model.AuthenticatedUser> build() {
|
Set<model.AuthenticatedUser> build() {
|
||||||
listenSelf((_, __) {
|
ref.listenSelf((_, __) {
|
||||||
writeStateToBox();
|
writeStateToBox();
|
||||||
});
|
});
|
||||||
// get the app settings
|
// get the app settings
|
||||||
|
|
@ -35,7 +35,7 @@ class AuthenticatedUsers extends _$AuthenticatedUsers {
|
||||||
Set<model.AuthenticatedUser> readFromBoxOrCreate() {
|
Set<model.AuthenticatedUser> readFromBoxOrCreate() {
|
||||||
if (_box.isNotEmpty) {
|
if (_box.isNotEmpty) {
|
||||||
final foundData = _box.getRange(0, _box.length);
|
final foundData = _box.getRange(0, _box.length);
|
||||||
_logger.fine('found users in box: ${foundData.obfuscate()}');
|
_logger.fine('found users in box: $foundData');
|
||||||
return foundData.toSet();
|
return foundData.toSet();
|
||||||
} else {
|
} else {
|
||||||
_logger.fine('no settings found in box');
|
_logger.fine('no settings found in box');
|
||||||
|
|
@ -49,17 +49,18 @@ class AuthenticatedUsers extends _$AuthenticatedUsers {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_box.addAll(state);
|
_box.addAll(state);
|
||||||
_logger.fine('writing state to box: ${state.obfuscate()}');
|
_logger.fine('writing state to box: $state');
|
||||||
}
|
}
|
||||||
|
|
||||||
void addUser(model.AuthenticatedUser user, {bool setActive = false}) {
|
void addUser(model.AuthenticatedUser user, {bool setActive = false}) {
|
||||||
state = state..add(user);
|
state = state..add(user);
|
||||||
ref.invalidateSelf();
|
|
||||||
if (setActive) {
|
if (setActive) {
|
||||||
final apiSettings = ref.read(apiSettingsProvider);
|
final apiSettings = ref.read(apiSettingsProvider);
|
||||||
ref
|
ref.read(apiSettingsProvider.notifier).updateState(
|
||||||
.read(apiSettingsProvider.notifier)
|
apiSettings.copyWith(
|
||||||
.updateState(apiSettings.copyWith(activeUser: user));
|
activeUser: user,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,12 +80,11 @@ class AuthenticatedUsers extends _$AuthenticatedUsers {
|
||||||
// also remove the user from the active user
|
// also remove the user from the active user
|
||||||
final apiSettings = ref.read(apiSettingsProvider);
|
final apiSettings = ref.read(apiSettingsProvider);
|
||||||
if (apiSettings.activeUser == user) {
|
if (apiSettings.activeUser == user) {
|
||||||
// replace the active user with the first user in the list
|
ref.read(apiSettingsProvider.notifier).updateState(
|
||||||
// or null if there are no users left
|
apiSettings.copyWith(
|
||||||
final newActiveUser = state.isNotEmpty ? state.first : null;
|
activeUser: null,
|
||||||
ref
|
),
|
||||||
.read(apiSettingsProvider.notifier)
|
);
|
||||||
.updateState(apiSettings.copyWith(activeUser: newActiveUser));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
28
lib/api/authenticated_user_provider.g.dart
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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<AuthenticatedUser,
|
||||||
|
Set<model.AuthenticatedUser>>.internal(
|
||||||
|
AuthenticatedUser.new,
|
||||||
|
name: r'authenticatedUserProvider',
|
||||||
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$authenticatedUserHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef _$AuthenticatedUser = AutoDisposeNotifier<Set<model.AuthenticatedUser>>;
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
// 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<AuthenticatedUsers, Set<model.AuthenticatedUser>> {
|
|
||||||
/// 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<model.AuthenticatedUser> value) {
|
|
||||||
return $ProviderOverride(
|
|
||||||
origin: this,
|
|
||||||
providerOverride: $SyncValueProvider<Set<model.AuthenticatedUser>>(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _$authenticatedUsersHash() =>
|
|
||||||
r'c5e82cc70ffc31a0d315e3db9e07a141c583471e';
|
|
||||||
|
|
||||||
/// provides with a set of authenticated users
|
|
||||||
|
|
||||||
abstract class _$AuthenticatedUsers
|
|
||||||
extends $Notifier<Set<model.AuthenticatedUser>> {
|
|
||||||
Set<model.AuthenticatedUser> build();
|
|
||||||
@$mustCallSuper
|
|
||||||
@override
|
|
||||||
void runBuild() {
|
|
||||||
final ref =
|
|
||||||
this.ref
|
|
||||||
as $Ref<Set<model.AuthenticatedUser>, Set<model.AuthenticatedUser>>;
|
|
||||||
final element =
|
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
|
||||||
AnyNotifier<
|
|
||||||
Set<model.AuthenticatedUser>,
|
|
||||||
Set<model.AuthenticatedUser>
|
|
||||||
>,
|
|
||||||
Set<model.AuthenticatedUser>,
|
|
||||||
Object?,
|
|
||||||
Object?
|
|
||||||
>;
|
|
||||||
element.handleCreate(ref, build);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -27,8 +27,7 @@ class CoverImage extends _$CoverImage {
|
||||||
// await Future.delayed(const Duration(seconds: 2));
|
// await Future.delayed(const Duration(seconds: 2));
|
||||||
|
|
||||||
// try to get the image from the cache
|
// try to get the image from the cache
|
||||||
final file =
|
final file = await imageCacheManager.getFileFromMemory(itemId) ??
|
||||||
await imageCacheManager.getFileFromMemory(itemId) ??
|
|
||||||
await imageCacheManager.getFileFromCache(itemId);
|
await imageCacheManager.getFileFromCache(itemId);
|
||||||
|
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
|
|
@ -45,7 +44,9 @@ class CoverImage extends _$CoverImage {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
_logger.fine('cover image stale for $itemId, fetching from the server');
|
_logger.fine(
|
||||||
|
'cover image stale for $itemId, fetching from the server',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_logger.fine('cover image not found in cache for $itemId');
|
_logger.fine('cover image not found in cache for $itemId');
|
||||||
|
|
|
||||||
|
|
@ -6,94 +6,167 @@ part of 'image_provider.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
String _$coverImageHash() => r'89cc4783cbc76bb41beae34384d92fb277135c75';
|
||||||
// ignore_for_file: type=lint, type=warning
|
|
||||||
|
|
||||||
@ProviderFor(CoverImage)
|
/// Copied from Dart SDK
|
||||||
final coverImageProvider = CoverImageFamily._();
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
final class CoverImageProvider
|
static int combine(int hash, int value) {
|
||||||
extends $StreamNotifierProvider<CoverImage, Uint8List> {
|
// ignore: parameter_assignments
|
||||||
CoverImageProvider._({
|
hash = 0x1fffffff & (hash + value);
|
||||||
required CoverImageFamily super.from,
|
// ignore: parameter_assignments
|
||||||
required String super.argument,
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
}) : super(
|
return hash ^ (hash >> 6);
|
||||||
retry: null,
|
}
|
||||||
name: r'coverImageProvider',
|
|
||||||
isAutoDispose: false,
|
static int finish(int hash) {
|
||||||
dependencies: null,
|
// ignore: parameter_assignments
|
||||||
$allTransitiveDependencies: null,
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
);
|
// ignore: parameter_assignments
|
||||||
|
hash = hash ^ (hash >> 11);
|
||||||
@override
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
String debugGetCreateSourceHash() => _$coverImageHash();
|
}
|
||||||
|
}
|
||||||
@override
|
|
||||||
String toString() {
|
abstract class _$CoverImage extends BuildlessStreamNotifier<Uint8List> {
|
||||||
return r'coverImageProvider'
|
late final String itemId;
|
||||||
''
|
|
||||||
'($argument)';
|
Stream<Uint8List> build(
|
||||||
|
String itemId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [CoverImage].
|
||||||
|
@ProviderFor(CoverImage)
|
||||||
|
const coverImageProvider = CoverImageFamily();
|
||||||
|
|
||||||
|
/// See also [CoverImage].
|
||||||
|
class CoverImageFamily extends Family<AsyncValue<Uint8List>> {
|
||||||
|
/// See also [CoverImage].
|
||||||
|
const CoverImageFamily();
|
||||||
|
|
||||||
|
/// See also [CoverImage].
|
||||||
|
CoverImageProvider call(
|
||||||
|
String itemId,
|
||||||
|
) {
|
||||||
|
return CoverImageProvider(
|
||||||
|
itemId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
@override
|
||||||
CoverImage create() => CoverImage();
|
CoverImageProvider getProviderOverride(
|
||||||
|
covariant CoverImageProvider provider,
|
||||||
|
) {
|
||||||
|
return call(
|
||||||
|
provider.itemId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'coverImageProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [CoverImage].
|
||||||
|
class CoverImageProvider
|
||||||
|
extends StreamNotifierProviderImpl<CoverImage, Uint8List> {
|
||||||
|
/// 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<Uint8List> 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<CoverImage, Uint8List> createElement() {
|
||||||
|
return _CoverImageProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return other is CoverImageProvider && other.argument == argument;
|
return other is CoverImageProvider && other.itemId == itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return argument.hashCode;
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, itemId.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$coverImageHash() => r'89cc4783cbc76bb41beae34384d92fb277135c75';
|
mixin CoverImageRef on StreamNotifierProviderRef<Uint8List> {
|
||||||
|
/// The parameter `itemId` of this provider.
|
||||||
|
String get itemId;
|
||||||
|
}
|
||||||
|
|
||||||
final class CoverImageFamily extends $Family
|
class _CoverImageProviderElement
|
||||||
with
|
extends StreamNotifierProviderElement<CoverImage, Uint8List>
|
||||||
$ClassFamilyOverride<
|
with CoverImageRef {
|
||||||
CoverImage,
|
_CoverImageProviderElement(super.provider);
|
||||||
AsyncValue<Uint8List>,
|
|
||||||
Uint8List,
|
|
||||||
Stream<Uint8List>,
|
|
||||||
String
|
|
||||||
> {
|
|
||||||
CoverImageFamily._()
|
|
||||||
: super(
|
|
||||||
retry: null,
|
|
||||||
name: r'coverImageProvider',
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
isAutoDispose: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
CoverImageProvider call(String itemId) =>
|
|
||||||
CoverImageProvider._(argument: itemId, from: this);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => r'coverImageProvider';
|
String get itemId => (origin as CoverImageProvider).itemId;
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _$CoverImage extends $StreamNotifier<Uint8List> {
|
|
||||||
late final _$args = ref.$arg as String;
|
|
||||||
String get itemId => _$args;
|
|
||||||
|
|
||||||
Stream<Uint8List> build(String itemId);
|
|
||||||
@$mustCallSuper
|
|
||||||
@override
|
|
||||||
void runBuild() {
|
|
||||||
final ref = this.ref as $Ref<AsyncValue<Uint8List>, Uint8List>;
|
|
||||||
final element =
|
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
|
||||||
AnyNotifier<AsyncValue<Uint8List>, Uint8List>,
|
|
||||||
AsyncValue<Uint8List>,
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,7 @@ class LibraryItem extends _$LibraryItem {
|
||||||
|
|
||||||
// look for the item in the cache
|
// look for the item in the cache
|
||||||
final key = CacheKey.libraryItem(id);
|
final key = CacheKey.libraryItem(id);
|
||||||
final cachedFile =
|
final cachedFile = await apiResponseCacheManager.getFileFromMemory(key) ??
|
||||||
await apiResponseCacheManager.getFileFromMemory(key) ??
|
|
||||||
await apiResponseCacheManager.getFileFromCache(key);
|
await apiResponseCacheManager.getFileFromCache(key);
|
||||||
if (cachedFile != null) {
|
if (cachedFile != null) {
|
||||||
_logger.fine(
|
_logger.fine(
|
||||||
|
|
|
||||||
|
|
@ -6,112 +6,182 @@ part of 'library_item_provider.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
String _$libraryItemHash() => r'a3cfa7f912e9498a70b5782899018b6964d6445c';
|
||||||
// ignore_for_file: type=lint, type=warning
|
|
||||||
/// provides the library item for the given id
|
|
||||||
|
|
||||||
@ProviderFor(LibraryItem)
|
/// Copied from Dart SDK
|
||||||
final libraryItemProvider = LibraryItemFamily._();
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
/// provides the library item for the given id
|
static int combine(int hash, int value) {
|
||||||
final class LibraryItemProvider
|
// ignore: parameter_assignments
|
||||||
extends $StreamNotifierProvider<LibraryItem, shelfsdk.LibraryItemExpanded> {
|
hash = 0x1fffffff & (hash + value);
|
||||||
/// provides the library item for the given id
|
// ignore: parameter_assignments
|
||||||
LibraryItemProvider._({
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
required LibraryItemFamily super.from,
|
return hash ^ (hash >> 6);
|
||||||
required String super.argument,
|
}
|
||||||
}) : super(
|
|
||||||
retry: null,
|
static int finish(int hash) {
|
||||||
name: r'libraryItemProvider',
|
// ignore: parameter_assignments
|
||||||
isAutoDispose: false,
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
dependencies: null,
|
// ignore: parameter_assignments
|
||||||
$allTransitiveDependencies: null,
|
hash = hash ^ (hash >> 11);
|
||||||
);
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
|
}
|
||||||
@override
|
}
|
||||||
String debugGetCreateSourceHash() => _$libraryItemHash();
|
|
||||||
|
abstract class _$LibraryItem
|
||||||
@override
|
extends BuildlessStreamNotifier<shelfsdk.LibraryItemExpanded> {
|
||||||
String toString() {
|
late final String id;
|
||||||
return r'libraryItemProvider'
|
|
||||||
''
|
Stream<shelfsdk.LibraryItemExpanded> build(
|
||||||
'($argument)';
|
String id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// provides the library item for the given id
|
||||||
|
///
|
||||||
|
/// Copied from [LibraryItem].
|
||||||
|
@ProviderFor(LibraryItem)
|
||||||
|
const libraryItemProvider = LibraryItemFamily();
|
||||||
|
|
||||||
|
/// provides the library item for the given id
|
||||||
|
///
|
||||||
|
/// Copied from [LibraryItem].
|
||||||
|
class LibraryItemFamily
|
||||||
|
extends Family<AsyncValue<shelfsdk.LibraryItemExpanded>> {
|
||||||
|
/// provides the library item for the given id
|
||||||
|
///
|
||||||
|
/// Copied from [LibraryItem].
|
||||||
|
const LibraryItemFamily();
|
||||||
|
|
||||||
|
/// provides the library item for the given id
|
||||||
|
///
|
||||||
|
/// Copied from [LibraryItem].
|
||||||
|
LibraryItemProvider call(
|
||||||
|
String id,
|
||||||
|
) {
|
||||||
|
return LibraryItemProvider(
|
||||||
|
id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
@override
|
||||||
LibraryItem create() => LibraryItem();
|
LibraryItemProvider getProviderOverride(
|
||||||
|
covariant LibraryItemProvider provider,
|
||||||
|
) {
|
||||||
|
return call(
|
||||||
|
provider.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'libraryItemProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// provides the library item for the given id
|
||||||
|
///
|
||||||
|
/// Copied from [LibraryItem].
|
||||||
|
class LibraryItemProvider extends StreamNotifierProviderImpl<LibraryItem,
|
||||||
|
shelfsdk.LibraryItemExpanded> {
|
||||||
|
/// 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<shelfsdk.LibraryItemExpanded> 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<LibraryItem, shelfsdk.LibraryItemExpanded>
|
||||||
|
createElement() {
|
||||||
|
return _LibraryItemProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return other is LibraryItemProvider && other.argument == argument;
|
return other is LibraryItemProvider && other.id == id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return argument.hashCode;
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, id.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$libraryItemHash() => r'a3cfa7f912e9498a70b5782899018b6964d6445c';
|
mixin LibraryItemRef
|
||||||
|
on StreamNotifierProviderRef<shelfsdk.LibraryItemExpanded> {
|
||||||
|
/// The parameter `id` of this provider.
|
||||||
|
String get id;
|
||||||
|
}
|
||||||
|
|
||||||
/// provides the library item for the given id
|
class _LibraryItemProviderElement extends StreamNotifierProviderElement<
|
||||||
|
LibraryItem, shelfsdk.LibraryItemExpanded> with LibraryItemRef {
|
||||||
final class LibraryItemFamily extends $Family
|
_LibraryItemProviderElement(super.provider);
|
||||||
with
|
|
||||||
$ClassFamilyOverride<
|
|
||||||
LibraryItem,
|
|
||||||
AsyncValue<shelfsdk.LibraryItemExpanded>,
|
|
||||||
shelfsdk.LibraryItemExpanded,
|
|
||||||
Stream<shelfsdk.LibraryItemExpanded>,
|
|
||||||
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
|
@override
|
||||||
String toString() => r'libraryItemProvider';
|
String get id => (origin as LibraryItemProvider).id;
|
||||||
}
|
|
||||||
|
|
||||||
/// provides the library item for the given id
|
|
||||||
|
|
||||||
abstract class _$LibraryItem
|
|
||||||
extends $StreamNotifier<shelfsdk.LibraryItemExpanded> {
|
|
||||||
late final _$args = ref.$arg as String;
|
|
||||||
String get id => _$args;
|
|
||||||
|
|
||||||
Stream<shelfsdk.LibraryItemExpanded> build(String id);
|
|
||||||
@$mustCallSuper
|
|
||||||
@override
|
|
||||||
void runBuild() {
|
|
||||||
final ref =
|
|
||||||
this.ref
|
|
||||||
as $Ref<
|
|
||||||
AsyncValue<shelfsdk.LibraryItemExpanded>,
|
|
||||||
shelfsdk.LibraryItemExpanded
|
|
||||||
>;
|
|
||||||
final element =
|
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
|
||||||
AnyNotifier<
|
|
||||||
AsyncValue<shelfsdk.LibraryItemExpanded>,
|
|
||||||
shelfsdk.LibraryItemExpanded
|
|
||||||
>,
|
|
||||||
AsyncValue<shelfsdk.LibraryItemExpanded>,
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
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?> 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<Library?> 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<List<Library>> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
// 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<AsyncValue<Library?>, Library?, FutureOr<Library?>>
|
|
||||||
with $FutureModifier<Library?>, $FutureProvider<Library?> {
|
|
||||||
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<Library?> $createElement($ProviderPointer pointer) =>
|
|
||||||
$FutureProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
|
||||||
FutureOr<Library?> 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<FutureOr<Library?>, 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<AsyncValue<Library?>, Library?, FutureOr<Library?>>
|
|
||||||
with $FutureModifier<Library?>, $FutureProvider<Library?> {
|
|
||||||
CurrentLibraryProvider._()
|
|
||||||
: super(
|
|
||||||
from: null,
|
|
||||||
argument: null,
|
|
||||||
retry: null,
|
|
||||||
name: r'currentLibraryProvider',
|
|
||||||
isAutoDispose: true,
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$currentLibraryHash();
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
$FutureProviderElement<Library?> $createElement($ProviderPointer pointer) =>
|
|
||||||
$FutureProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
|
||||||
FutureOr<Library?> create(Ref ref) {
|
|
||||||
return currentLibrary(ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _$currentLibraryHash() => r'658498a531e04a01e2b3915a3319101285601118';
|
|
||||||
|
|
||||||
@ProviderFor(Libraries)
|
|
||||||
final librariesProvider = LibrariesProvider._();
|
|
||||||
|
|
||||||
final class LibrariesProvider
|
|
||||||
extends $AsyncNotifierProvider<Libraries, List<Library>> {
|
|
||||||
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<List<Library>> {
|
|
||||||
FutureOr<List<Library>> build();
|
|
||||||
@$mustCallSuper
|
|
||||||
@override
|
|
||||||
void runBuild() {
|
|
||||||
final ref = this.ref as $Ref<AsyncValue<List<Library>>, List<Library>>;
|
|
||||||
final element =
|
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
|
||||||
AnyNotifier<AsyncValue<List<Library>>, List<Library>>,
|
|
||||||
AsyncValue<List<Library>>,
|
|
||||||
Object?,
|
|
||||||
Object?
|
|
||||||
>;
|
|
||||||
element.handleCreate(ref, build);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
import 'package:logging/logging.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:vaani/api/authenticated_users_provider.dart';
|
import 'package:vaani/api/authenticated_user_provider.dart';
|
||||||
import 'package:vaani/db/storage.dart';
|
import 'package:vaani/db/storage.dart';
|
||||||
import 'package:vaani/settings/api_settings_provider.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'
|
||||||
import 'package:vaani/shared/extensions/obfuscation.dart';
|
as model;
|
||||||
|
|
||||||
part 'server_provider.g.dart';
|
part 'server_provider.g.dart';
|
||||||
|
|
||||||
final _box = AvailableHiveBoxes.serverBox;
|
final _box = AvailableHiveBoxes.serverBox;
|
||||||
|
|
||||||
final _logger = Logger('AudiobookShelfServerProvider');
|
|
||||||
|
|
||||||
class ServerAlreadyExistsException implements Exception {
|
class ServerAlreadyExistsException implements Exception {
|
||||||
final model.AudiobookShelfServer server;
|
final model.AudiobookShelfServer server;
|
||||||
|
|
||||||
|
|
@ -28,7 +27,7 @@ class ServerAlreadyExistsException implements Exception {
|
||||||
class AudiobookShelfServer extends _$AudiobookShelfServer {
|
class AudiobookShelfServer extends _$AudiobookShelfServer {
|
||||||
@override
|
@override
|
||||||
Set<model.AudiobookShelfServer> build() {
|
Set<model.AudiobookShelfServer> build() {
|
||||||
listenSelf((_, __) {
|
ref.listenSelf((_, __) {
|
||||||
writeStateToBox();
|
writeStateToBox();
|
||||||
});
|
});
|
||||||
// get the app settings
|
// get the app settings
|
||||||
|
|
@ -48,10 +47,10 @@ class AudiobookShelfServer extends _$AudiobookShelfServer {
|
||||||
Set<model.AudiobookShelfServer> readFromBoxOrCreate() {
|
Set<model.AudiobookShelfServer> readFromBoxOrCreate() {
|
||||||
if (_box.isNotEmpty) {
|
if (_box.isNotEmpty) {
|
||||||
final foundServers = _box.getRange(0, _box.length);
|
final foundServers = _box.getRange(0, _box.length);
|
||||||
_logger.info('found servers in box: ${foundServers.obfuscate()}');
|
debugPrint('found servers in box: $foundServers');
|
||||||
return foundServers.nonNulls.toSet();
|
return foundServers.whereNotNull().toSet();
|
||||||
} else {
|
} else {
|
||||||
_logger.info('no settings found in box');
|
debugPrint('no settings found in box');
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +61,7 @@ class AudiobookShelfServer extends _$AudiobookShelfServer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_box.addAll(state);
|
_box.addAll(state);
|
||||||
_logger.info('writing state to box: ${state.obfuscate()}');
|
debugPrint('writing state to box: $state');
|
||||||
}
|
}
|
||||||
|
|
||||||
void addServer(model.AudiobookShelfServer server) {
|
void addServer(model.AudiobookShelfServer server) {
|
||||||
|
|
@ -72,21 +71,23 @@ class AudiobookShelfServer extends _$AudiobookShelfServer {
|
||||||
state = {...state, server};
|
state = {...state, server};
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeServer(
|
void removeServer(model.AudiobookShelfServer server,
|
||||||
model.AudiobookShelfServer server, {
|
{
|
||||||
bool removeUsers = false,
|
bool removeUsers = false,
|
||||||
}) {
|
}) {
|
||||||
state = state.where((s) => s != server).toSet();
|
state = state.where((s) => s != server).toSet();
|
||||||
// remove the server from the active server
|
// remove the server from the active server
|
||||||
final apiSettings = ref.read(apiSettingsProvider);
|
final apiSettings = ref.read(apiSettingsProvider);
|
||||||
if (apiSettings.activeServer == server) {
|
if (apiSettings.activeServer == server) {
|
||||||
ref
|
ref.read(apiSettingsProvider.notifier).updateState(
|
||||||
.read(apiSettingsProvider.notifier)
|
apiSettings.copyWith(
|
||||||
.updateState(apiSettings.copyWith(activeServer: null));
|
activeServer: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// remove the users of this server
|
// remove the users of this server
|
||||||
if (removeUsers) {
|
if (removeUsers) {
|
||||||
ref.read(authenticatedUsersProvider.notifier).removeUsersOfServer(server);
|
ref.read(authenticatedUserProvider.notifier).removeUsersOfServer(server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,78 +6,25 @@ part of 'server_provider.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// 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
|
|
||||||
final class AudiobookShelfServerProvider
|
|
||||||
extends
|
|
||||||
$NotifierProvider<
|
|
||||||
AudiobookShelfServer,
|
|
||||||
Set<model.AudiobookShelfServer>
|
|
||||||
> {
|
|
||||||
/// 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,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$audiobookShelfServerHash();
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
AudiobookShelfServer create() => AudiobookShelfServer();
|
|
||||||
|
|
||||||
/// {@macro riverpod.override_with_value}
|
|
||||||
Override overrideWithValue(Set<model.AudiobookShelfServer> value) {
|
|
||||||
return $ProviderOverride(
|
|
||||||
origin: this,
|
|
||||||
providerOverride: $SyncValueProvider<Set<model.AudiobookShelfServer>>(
|
|
||||||
value,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _$audiobookShelfServerHash() =>
|
String _$audiobookShelfServerHash() =>
|
||||||
r'144817dcb3704b80c5b60763167fcf932f00c29c';
|
r'f0d645bb42233c59886bc43fdc473897484ceca1';
|
||||||
|
|
||||||
/// provides with a set of servers added by the user
|
/// provides with a set of servers added by the user
|
||||||
|
///
|
||||||
|
/// Copied from [AudiobookShelfServer].
|
||||||
|
@ProviderFor(AudiobookShelfServer)
|
||||||
|
final audiobookShelfServerProvider = AutoDisposeNotifierProvider<
|
||||||
|
AudiobookShelfServer, Set<model.AudiobookShelfServer>>.internal(
|
||||||
|
AudiobookShelfServer.new,
|
||||||
|
name: r'audiobookShelfServerProvider',
|
||||||
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$audiobookShelfServerHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
abstract class _$AudiobookShelfServer
|
typedef _$AudiobookShelfServer
|
||||||
extends $Notifier<Set<model.AudiobookShelfServer>> {
|
= AutoDisposeNotifier<Set<model.AudiobookShelfServer>>;
|
||||||
Set<model.AudiobookShelfServer> build();
|
// ignore_for_file: type=lint
|
||||||
@$mustCallSuper
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||||
@override
|
|
||||||
void runBuild() {
|
|
||||||
final ref =
|
|
||||||
this.ref
|
|
||||||
as $Ref<
|
|
||||||
Set<model.AudiobookShelfServer>,
|
|
||||||
Set<model.AudiobookShelfServer>
|
|
||||||
>;
|
|
||||||
final element =
|
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
|
||||||
AnyNotifier<
|
|
||||||
Set<model.AudiobookShelfServer>,
|
|
||||||
Set<model.AudiobookShelfServer>
|
|
||||||
>,
|
|
||||||
Set<model.AudiobookShelfServer>,
|
|
||||||
Object?,
|
|
||||||
Object?
|
|
||||||
>;
|
|
||||||
element.handleCreate(ref, build);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,5 @@ class HeroTagPrefixes {
|
||||||
static const String bookTitle = 'book_title_';
|
static const String bookTitle = 'book_title_';
|
||||||
static const String narratorName = 'narrator_name_';
|
static const String narratorName = 'narrator_name_';
|
||||||
static const String libraryItemPlayButton = 'library_item_play_button_';
|
static const String libraryItemPlayButton = 'library_item_play_button_';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:flutter/foundation.dart' show immutable;
|
import 'package:flutter/foundation.dart' show immutable;
|
||||||
import 'package:hive_plus_secure/hive_plus_secure.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:vaani/features/per_book_settings/models/book_settings.dart';
|
import 'package:vaani/features/per_book_settings/models/book_settings.dart';
|
||||||
import 'package:vaani/settings/models/models.dart';
|
import 'package:vaani/settings/models/models.dart';
|
||||||
|
|
||||||
|
|
@ -14,17 +14,14 @@ class AvailableHiveBoxes {
|
||||||
static final apiSettingsBox = Hive.box<ApiSettings>(name: 'apiSettings');
|
static final apiSettingsBox = Hive.box<ApiSettings>(name: 'apiSettings');
|
||||||
|
|
||||||
/// stores the a list of [AudiobookShelfServer]
|
/// stores the a list of [AudiobookShelfServer]
|
||||||
static final serverBox = Hive.box<AudiobookShelfServer>(
|
static final serverBox =
|
||||||
name: 'audiobookShelfServer',
|
Hive.box<AudiobookShelfServer>(name: 'audiobookShelfServer');
|
||||||
);
|
|
||||||
|
|
||||||
/// stores the a list of [AuthenticatedUser]
|
/// stores the a list of [AuthenticatedUser]
|
||||||
static final authenticatedUserBox = Hive.box<AuthenticatedUser>(
|
static final authenticatedUserBox =
|
||||||
name: 'authenticatedUser',
|
Hive.box<AuthenticatedUser>(name: 'authenticatedUser');
|
||||||
);
|
|
||||||
|
|
||||||
/// stores the a list of [BookSettings]
|
/// stores the a list of [BookSettings]
|
||||||
static final individualBookSettingsBox = Hive.box<BookSettings>(
|
static final individualBookSettingsBox =
|
||||||
name: 'bookSettings',
|
Hive.box<BookSettings>(name: 'bookSettings');
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
66
lib/db/cache/schemas/image.dart
vendored
|
|
@ -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
|
/// Represents a cover image for a library item
|
||||||
// ///
|
///
|
||||||
// /// stores 2 paths, one is thumbnail and the other is the full size image
|
/// stores 2 paths, one is thumbnail and the other is the full size image
|
||||||
// /// both are optional
|
/// both are optional
|
||||||
// /// also stores last fetched date for the image
|
/// also stores last fetched date for the image
|
||||||
// /// Id is passed as a parameter to the collection annotation (the lib_item_id)
|
/// Id is passed as a parameter to the collection annotation (the lib_item_id)
|
||||||
// /// also index the id
|
/// also index the id
|
||||||
// /// This is because the image is a part of the library item and the library item
|
/// This is because the image is a part of the library item and the library item
|
||||||
// /// is the parent of the image
|
/// is the parent of the image
|
||||||
// @Collection(ignore: {'path'})
|
@Collection(ignore: {'path'})
|
||||||
// @Name('CacheImage')
|
@Name('CacheImage')
|
||||||
// class Image {
|
class Image {
|
||||||
// @Id()
|
@Id()
|
||||||
// int id;
|
int id;
|
||||||
|
|
||||||
// String? thumbnailPath;
|
String? thumbnailPath;
|
||||||
// String? imagePath;
|
String? imagePath;
|
||||||
// DateTime lastSaved;
|
DateTime lastSaved;
|
||||||
|
|
||||||
// Image({
|
Image({
|
||||||
// required this.id,
|
required this.id,
|
||||||
// this.thumbnailPath,
|
this.thumbnailPath,
|
||||||
// this.imagePath,
|
this.imagePath,
|
||||||
// }) : lastSaved = DateTime.now();
|
}) : lastSaved = DateTime.now();
|
||||||
|
|
||||||
// /// returns the path to the image
|
/// returns the path to the image
|
||||||
// String? get path => thumbnailPath ?? imagePath;
|
String? get path => thumbnailPath ?? imagePath;
|
||||||
|
|
||||||
// /// automatically updates the last fetched date when saving a new path
|
/// automatically updates the last fetched date when saving a new path
|
||||||
// void updatePath(String? thumbnailPath, String? imagePath) async {
|
void updatePath(String? thumbnailPath, String? imagePath) async {
|
||||||
// this.thumbnailPath = thumbnailPath;
|
this.thumbnailPath = thumbnailPath;
|
||||||
// this.imagePath = imagePath;
|
this.imagePath = imagePath;
|
||||||
// lastSaved = DateTime.now();
|
lastSaved = DateTime.now();
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
|
||||||
1009
lib/db/cache/schemas/image.g.dart
vendored
Normal file
|
|
@ -1,23 +1,28 @@
|
||||||
|
// does the initial setup of the storage
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:hive_plus_secure/hive_plus_secure.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:vaani/main.dart';
|
|
||||||
import 'package:vaani/settings/constants.dart';
|
import 'package:vaani/settings/constants.dart';
|
||||||
|
|
||||||
import 'register_models.dart';
|
import 'register_models.dart';
|
||||||
|
|
||||||
// does the initial setup of the storage
|
|
||||||
Future initStorage() async {
|
Future initStorage() async {
|
||||||
final dir = await getApplicationDocumentsDirectory();
|
final dir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
// use vaani as the directory for hive
|
// 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);
|
await storageDir.create(recursive: true);
|
||||||
|
|
||||||
Hive.defaultDirectory = storageDir.path;
|
Hive.defaultDirectory = storageDir.path;
|
||||||
appLogger.config('Hive storage directory init: ${Hive.defaultDirectory}');
|
debugPrint('Hive storage directory init: ${Hive.defaultDirectory}');
|
||||||
|
|
||||||
await registerModels();
|
await registerModels();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
// // a table to track preferences of player for each book
|
// a table to track preferences of player for each book
|
||||||
// import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
// part 'book_prefs.g.dart';
|
part 'book_prefs.g.dart';
|
||||||
|
|
||||||
// /// stores the preferences of the player for a book
|
/// stores the preferences of the player for a book
|
||||||
// @Collection()
|
@Collection()
|
||||||
// @Name('BookPrefs')
|
@Name('BookPrefs')
|
||||||
// class BookPrefs {
|
class BookPrefs {
|
||||||
// @Id()
|
@Id()
|
||||||
// int libItemId;
|
int libItemId;
|
||||||
|
|
||||||
// double? speed;
|
double? speed;
|
||||||
// // double? volume;
|
// double? volume;
|
||||||
// // Duration? sleepTimer;
|
// Duration? sleepTimer;
|
||||||
// // bool? showTotalProgress;
|
// bool? showTotalProgress;
|
||||||
// // bool? showChapterProgress;
|
// bool? showChapterProgress;
|
||||||
// // bool? useChapterInfo;
|
// bool? useChapterInfo;
|
||||||
|
|
||||||
// BookPrefs({
|
BookPrefs({
|
||||||
// required this.libItemId,
|
required this.libItemId,
|
||||||
// this.speed,
|
this.speed,
|
||||||
// // this.volume,
|
// this.volume,
|
||||||
// // this.sleepTimer,
|
// this.sleepTimer,
|
||||||
// // this.showTotalProgress,
|
// this.showTotalProgress,
|
||||||
// // this.showChapterProgress,
|
// this.showChapterProgress,
|
||||||
// // this.useChapterInfo,
|
// this.useChapterInfo,
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
|
|
|
||||||
496
lib/db/player_prefs/book_prefs.g.dart
Normal file
|
|
@ -0,0 +1,496 @@
|
||||||
|
// 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<int, BookPrefs> 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<int, BookPrefs>(
|
||||||
|
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<int, BookPrefs> 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<int> libItemId,
|
||||||
|
double? speed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BookPrefsUpdateAllImpl implements _BookPrefsUpdateAll {
|
||||||
|
const _BookPrefsUpdateAllImpl(this.collection);
|
||||||
|
|
||||||
|
final IsarCollection<int, BookPrefs> collection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int call({
|
||||||
|
required List<int> libItemId,
|
||||||
|
Object? speed = ignore,
|
||||||
|
}) {
|
||||||
|
return collection.updateProperties(libItemId, {
|
||||||
|
if (speed != ignore) 1: speed as double?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BookPrefsUpdate on IsarCollection<int, BookPrefs> {
|
||||||
|
_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<BookPrefs> 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<BookPrefs> {
|
||||||
|
_BookPrefsQueryUpdate get updateFirst =>
|
||||||
|
_BookPrefsQueryUpdateImpl(this, limit: 1);
|
||||||
|
|
||||||
|
_BookPrefsQueryUpdate get updateAll => _BookPrefsQueryUpdateImpl(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BookPrefsQueryBuilderUpdateImpl implements _BookPrefsQueryUpdate {
|
||||||
|
const _BookPrefsQueryBuilderUpdateImpl(this.query, {this.limit});
|
||||||
|
|
||||||
|
final QueryBuilder<BookPrefs, BookPrefs, QOperations> 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<BookPrefs, BookPrefs, QOperations> {
|
||||||
|
_BookPrefsQueryUpdate get updateFirst =>
|
||||||
|
_BookPrefsQueryBuilderUpdateImpl(this, limit: 1);
|
||||||
|
|
||||||
|
_BookPrefsQueryUpdate get updateAll => _BookPrefsQueryBuilderUpdateImpl(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BookPrefsQueryFilter
|
||||||
|
on QueryBuilder<BookPrefs, BookPrefs, QFilterCondition> {
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition> libItemIdEqualTo(
|
||||||
|
int value,
|
||||||
|
) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
EqualCondition(
|
||||||
|
property: 0,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition>
|
||||||
|
libItemIdGreaterThan(
|
||||||
|
int value,
|
||||||
|
) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
GreaterCondition(
|
||||||
|
property: 0,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition>
|
||||||
|
libItemIdGreaterThanOrEqualTo(
|
||||||
|
int value,
|
||||||
|
) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
GreaterOrEqualCondition(
|
||||||
|
property: 0,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition> libItemIdLessThan(
|
||||||
|
int value,
|
||||||
|
) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
LessCondition(
|
||||||
|
property: 0,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition>
|
||||||
|
libItemIdLessThanOrEqualTo(
|
||||||
|
int value,
|
||||||
|
) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
LessOrEqualCondition(
|
||||||
|
property: 0,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition> libItemIdBetween(
|
||||||
|
int lower,
|
||||||
|
int upper,
|
||||||
|
) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
BetweenCondition(
|
||||||
|
property: 0,
|
||||||
|
lower: lower,
|
||||||
|
upper: upper,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition> speedIsNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const IsNullCondition(property: 1));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition> speedIsNotNull() {
|
||||||
|
return QueryBuilder.apply(not(), (query) {
|
||||||
|
return query.addFilterCondition(const IsNullCondition(property: 1));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition> speedEqualTo(
|
||||||
|
double? value, {
|
||||||
|
double epsilon = Filter.epsilon,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
EqualCondition(
|
||||||
|
property: 1,
|
||||||
|
value: value,
|
||||||
|
epsilon: epsilon,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition> speedGreaterThan(
|
||||||
|
double? value, {
|
||||||
|
double epsilon = Filter.epsilon,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
GreaterCondition(
|
||||||
|
property: 1,
|
||||||
|
value: value,
|
||||||
|
epsilon: epsilon,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition>
|
||||||
|
speedGreaterThanOrEqualTo(
|
||||||
|
double? value, {
|
||||||
|
double epsilon = Filter.epsilon,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
GreaterOrEqualCondition(
|
||||||
|
property: 1,
|
||||||
|
value: value,
|
||||||
|
epsilon: epsilon,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition> speedLessThan(
|
||||||
|
double? value, {
|
||||||
|
double epsilon = Filter.epsilon,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
LessCondition(
|
||||||
|
property: 1,
|
||||||
|
value: value,
|
||||||
|
epsilon: epsilon,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition>
|
||||||
|
speedLessThanOrEqualTo(
|
||||||
|
double? value, {
|
||||||
|
double epsilon = Filter.epsilon,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(
|
||||||
|
LessOrEqualCondition(
|
||||||
|
property: 1,
|
||||||
|
value: value,
|
||||||
|
epsilon: epsilon,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterFilterCondition> 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<BookPrefs, BookPrefs, QFilterCondition> {}
|
||||||
|
|
||||||
|
extension BookPrefsQuerySortBy on QueryBuilder<BookPrefs, BookPrefs, QSortBy> {
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterSortBy> sortByLibItemId() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterSortBy> sortByLibItemIdDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(0, sort: Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterSortBy> sortBySpeed() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterSortBy> sortBySpeedDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(1, sort: Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BookPrefsQuerySortThenBy
|
||||||
|
on QueryBuilder<BookPrefs, BookPrefs, QSortThenBy> {
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterSortBy> thenByLibItemId() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterSortBy> thenByLibItemIdDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(0, sort: Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterSortBy> thenBySpeed() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterSortBy> thenBySpeedDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(1, sort: Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BookPrefsQueryWhereDistinct
|
||||||
|
on QueryBuilder<BookPrefs, BookPrefs, QDistinct> {
|
||||||
|
QueryBuilder<BookPrefs, BookPrefs, QAfterDistinct> distinctBySpeed() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addDistinctBy(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BookPrefsQueryProperty1
|
||||||
|
on QueryBuilder<BookPrefs, BookPrefs, QProperty> {
|
||||||
|
QueryBuilder<BookPrefs, int, QAfterProperty> libItemIdProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addProperty(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, double?, QAfterProperty> speedProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addProperty(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BookPrefsQueryProperty2<R>
|
||||||
|
on QueryBuilder<BookPrefs, R, QAfterProperty> {
|
||||||
|
QueryBuilder<BookPrefs, (R, int), QAfterProperty> libItemIdProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addProperty(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, (R, double?), QAfterProperty> speedProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addProperty(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BookPrefsQueryProperty3<R1, R2>
|
||||||
|
on QueryBuilder<BookPrefs, (R1, R2), QAfterProperty> {
|
||||||
|
QueryBuilder<BookPrefs, (R1, R2, int), QOperations> libItemIdProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addProperty(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<BookPrefs, (R1, R2, double?), QOperations> speedProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addProperty(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:hive_plus_secure/hive_plus_secure.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:vaani/features/per_book_settings/models/book_settings.dart';
|
import 'package:vaani/features/per_book_settings/models/book_settings.dart';
|
||||||
import 'package:vaani/settings/models/models.dart';
|
import 'package:vaani/settings/models/models.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import 'package:logging/logging.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shelfsdk/audiobookshelf_api.dart';
|
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||||
import 'package:vaani/shared/extensions/model_conversions.dart';
|
import 'package:vaani/shared/extensions/model_conversions.dart';
|
||||||
import 'package:vaani/shared/extensions/obfuscation.dart';
|
|
||||||
|
|
||||||
final _logger = Logger('AudiobookDownloadManager');
|
final _logger = Logger('AudiobookDownloadManager');
|
||||||
final tq = MemoryTaskQueue();
|
final tq = MemoryTaskQueue();
|
||||||
|
|
@ -36,9 +35,7 @@ class AudiobookDownloadManager {
|
||||||
|
|
||||||
FileDownloader().addTaskQueue(tq);
|
FileDownloader().addTaskQueue(tq);
|
||||||
|
|
||||||
_logger.fine(
|
_logger.fine('initialized with baseUrl: $baseUrl, token: $token');
|
||||||
'initialized with baseUrl: ${Uri.parse(baseUrl).obfuscate()} and token: ${token.obfuscate()}',
|
|
||||||
);
|
|
||||||
_logger.fine(
|
_logger.fine(
|
||||||
'requiresWiFi: $requiresWiFi, retries: $retries, allowPause: $allowPause',
|
'requiresWiFi: $requiresWiFi, retries: $retries, allowPause: $allowPause',
|
||||||
);
|
);
|
||||||
|
|
@ -67,13 +64,17 @@ class AudiobookDownloadManager {
|
||||||
|
|
||||||
late StreamSubscription<TaskUpdate> _updatesSubscription;
|
late StreamSubscription<TaskUpdate> _updatesSubscription;
|
||||||
|
|
||||||
Future<void> queueAudioBookDownload(LibraryItemExpanded item) async {
|
Future<void> queueAudioBookDownload(
|
||||||
|
LibraryItemExpanded item,
|
||||||
|
) async {
|
||||||
_logger.info('queuing download for item: ${item.id}');
|
_logger.info('queuing download for item: ${item.id}');
|
||||||
// create a download task for each file in the item
|
// create a download task for each file in the item
|
||||||
final directory = await getApplicationSupportDirectory();
|
final directory = await getApplicationSupportDirectory();
|
||||||
for (final file in item.libraryFiles) {
|
for (final file in item.libraryFiles) {
|
||||||
// check if the file is already downloaded
|
// 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}');
|
_logger.info('file already downloaded: ${file.metadata.filename}');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +102,8 @@ class AudiobookDownloadManager {
|
||||||
Directory directory,
|
Directory directory,
|
||||||
LibraryItemExpanded item,
|
LibraryItemExpanded item,
|
||||||
LibraryFile file,
|
LibraryFile file,
|
||||||
) => '${directory.path}/${item.relPath}/${file.metadata.filename}';
|
) =>
|
||||||
|
'${directory.path}/${item.relPath}/${file.metadata.filename}';
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_updatesSubscription.cancel();
|
_updatesSubscription.cancel();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:background_downloader/background_downloader.dart';
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:shelfsdk/audiobookshelf_api.dart';
|
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||||
|
|
@ -32,11 +31,9 @@ class SimpleDownloadManager extends _$SimpleDownloadManager {
|
||||||
core.tq.maxConcurrentByGroup = downloadSettings.maxConcurrentByGroup;
|
core.tq.maxConcurrentByGroup = downloadSettings.maxConcurrentByGroup;
|
||||||
|
|
||||||
ref.onDispose(() {
|
ref.onDispose(() {
|
||||||
_logger.info('disposing download manager');
|
|
||||||
manager.dispose();
|
manager.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
_logger.config('initialized download manager');
|
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,13 +49,15 @@ class DownloadManager extends _$DownloadManager {
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> queueAudioBookDownload(LibraryItemExpanded item) async {
|
Future<void> queueAudioBookDownload(
|
||||||
_logger.fine('queueing download for ${item.id}');
|
LibraryItemExpanded item,
|
||||||
await state.queueAudioBookDownload(item);
|
) async {
|
||||||
|
await state.queueAudioBookDownload(
|
||||||
|
item,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteDownloadedItem(LibraryItemExpanded item) async {
|
Future<void> deleteDownloadedItem(LibraryItemExpanded item) async {
|
||||||
_logger.fine('deleting downloaded item ${item.id}');
|
|
||||||
await state.deleteDownloadedItem(item);
|
await state.deleteDownloadedItem(item);
|
||||||
ref.notifyListeners();
|
ref.notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
@ -79,16 +78,14 @@ class ItemDownloadProgress extends _$ItemDownloadProgress {
|
||||||
Future<double?> build(String id) async {
|
Future<double?> build(String id) async {
|
||||||
final item = await ref.watch(libraryItemProvider(id).future);
|
final item = await ref.watch(libraryItemProvider(id).future);
|
||||||
final manager = ref.read(downloadManagerProvider);
|
final manager = ref.read(downloadManagerProvider);
|
||||||
manager.taskUpdateStream
|
manager.taskUpdateStream.map((taskUpdate) {
|
||||||
.map((taskUpdate) {
|
|
||||||
if (taskUpdate is! TaskProgressUpdate) {
|
if (taskUpdate is! TaskProgressUpdate) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (taskUpdate.task.group == id) {
|
if (taskUpdate.task.group == id) {
|
||||||
return taskUpdate;
|
return taskUpdate;
|
||||||
}
|
}
|
||||||
})
|
}).listen((task) async {
|
||||||
.listen((task) async {
|
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
final totalSize = item.totalSize;
|
final totalSize = item.totalSize;
|
||||||
// if total size is 0, return 0
|
// if total size is 0, return 0
|
||||||
|
|
@ -96,9 +93,7 @@ class ItemDownloadProgress extends _$ItemDownloadProgress {
|
||||||
state = const AsyncValue.data(0.0);
|
state = const AsyncValue.data(0.0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final downloadedFiles = await manager.getDownloadedFilesMetadata(
|
final downloadedFiles = await manager.getDownloadedFilesMetadata(item);
|
||||||
item,
|
|
||||||
);
|
|
||||||
// calculate total size of downloaded files and total size of item, then divide
|
// calculate total size of downloaded files and total size of item, then divide
|
||||||
// to get percentage
|
// to get percentage
|
||||||
final downloadedSize = downloadedFiles.fold<int>(
|
final downloadedSize = downloadedFiles.fold<int>(
|
||||||
|
|
@ -110,7 +105,7 @@ class ItemDownloadProgress extends _$ItemDownloadProgress {
|
||||||
final totalDownloadedSize = downloadedSize + inProgressFileSize;
|
final totalDownloadedSize = downloadedSize + inProgressFileSize;
|
||||||
final progress = totalDownloadedSize / totalSize;
|
final progress = totalDownloadedSize / totalSize;
|
||||||
// if current progress is more than calculated progress, do not update
|
// if current progress is more than calculated progress, do not update
|
||||||
if (progress < (state.value ?? 0.0)) {
|
if (progress < (state.valueOrNull ?? 0.0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,14 +117,19 @@ class ItemDownloadProgress extends _$ItemDownloadProgress {
|
||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
FutureOr<List<TaskRecord>> downloadHistory(Ref ref, {String? group}) async {
|
FutureOr<List<TaskRecord>> downloadHistory(
|
||||||
|
DownloadHistoryRef ref, {
|
||||||
|
String? group,
|
||||||
|
}) async {
|
||||||
return await FileDownloader().database.allRecords(group: group);
|
return await FileDownloader().database.allRecords(group: group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
class IsItemDownloaded extends _$IsItemDownloaded {
|
class IsItemDownloaded extends _$IsItemDownloaded {
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> build(LibraryItemExpanded item) {
|
FutureOr<bool> build(
|
||||||
|
LibraryItemExpanded item,
|
||||||
|
) {
|
||||||
final manager = ref.watch(downloadManagerProvider);
|
final manager = ref.watch(downloadManagerProvider);
|
||||||
return manager.isItemDownloaded(item);
|
return manager.isItemDownloaded(item);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,10 @@ class DownloadsPage extends HookConsumerWidget {
|
||||||
final downloadHistory = ref.watch(downloadHistoryProvider());
|
final downloadHistory = ref.watch(downloadHistoryProvider());
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Downloads')),
|
appBar: AppBar(
|
||||||
|
title: const Text('Downloads'),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
// history of downloads
|
// history of downloads
|
||||||
child: downloadHistory.when(
|
child: downloadHistory.when(
|
||||||
|
|
|
||||||
|
|
@ -6,64 +6,24 @@ part of 'search_controller.dart';
|
||||||
// RiverpodGenerator
|
// 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<GlobalSearchController, Raw<SearchController>> {
|
|
||||||
/// 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<SearchController> value) {
|
|
||||||
return $ProviderOverride(
|
|
||||||
origin: this,
|
|
||||||
providerOverride: $SyncValueProvider<Raw<SearchController>>(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _$globalSearchControllerHash() =>
|
String _$globalSearchControllerHash() =>
|
||||||
r'd854ace6f2e00a10fc33aba63051375f82ad1b10';
|
r'd854ace6f2e00a10fc33aba63051375f82ad1b10';
|
||||||
|
|
||||||
/// The controller for the search bar.
|
/// The controller for the search bar.
|
||||||
|
///
|
||||||
|
/// Copied from [GlobalSearchController].
|
||||||
|
@ProviderFor(GlobalSearchController)
|
||||||
|
final globalSearchControllerProvider =
|
||||||
|
NotifierProvider<GlobalSearchController, Raw<SearchController>>.internal(
|
||||||
|
GlobalSearchController.new,
|
||||||
|
name: r'globalSearchControllerProvider',
|
||||||
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$globalSearchControllerHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
abstract class _$GlobalSearchController
|
typedef _$GlobalSearchController = Notifier<Raw<SearchController>>;
|
||||||
extends $Notifier<Raw<SearchController>> {
|
// ignore_for_file: type=lint
|
||||||
Raw<SearchController> build();
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||||
@$mustCallSuper
|
|
||||||
@override
|
|
||||||
void runBuild() {
|
|
||||||
final ref = this.ref as $Ref<Raw<SearchController>, Raw<SearchController>>;
|
|
||||||
final element =
|
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
|
||||||
AnyNotifier<Raw<SearchController>, Raw<SearchController>>,
|
|
||||||
Raw<SearchController>,
|
|
||||||
Object?,
|
|
||||||
Object?
|
|
||||||
>;
|
|
||||||
element.handleCreate(ref, build);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:shelfsdk/audiobookshelf_api.dart';
|
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||||
import 'package:vaani/api/api_provider.dart';
|
import 'package:vaani/api/api_provider.dart';
|
||||||
|
|
@ -9,7 +8,7 @@ part 'search_result_provider.g.dart';
|
||||||
/// The provider for the search result.
|
/// The provider for the search result.
|
||||||
@riverpod
|
@riverpod
|
||||||
FutureOr<LibrarySearchResponse?> searchResult(
|
FutureOr<LibrarySearchResponse?> searchResult(
|
||||||
Ref ref,
|
SearchResultRef ref,
|
||||||
String query, {
|
String query, {
|
||||||
int limit = 25,
|
int limit = 25,
|
||||||
}) async {
|
}) async {
|
||||||
|
|
|
||||||
|
|
@ -6,94 +6,184 @@ part of 'search_result_provider.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
String _$searchResultHash() => r'9baa643cce24f3a5e022f42202e423373939ef95';
|
||||||
// ignore_for_file: type=lint, type=warning
|
|
||||||
/// The provider for the search result.
|
|
||||||
|
|
||||||
@ProviderFor(searchResult)
|
/// Copied from Dart SDK
|
||||||
final searchResultProvider = SearchResultFamily._();
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
/// The provider for the search result.
|
static int combine(int hash, int value) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
final class SearchResultProvider
|
hash = 0x1fffffff & (hash + value);
|
||||||
extends
|
// ignore: parameter_assignments
|
||||||
$FunctionalProvider<
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
AsyncValue<LibrarySearchResponse?>,
|
return hash ^ (hash >> 6);
|
||||||
LibrarySearchResponse?,
|
|
||||||
FutureOr<LibrarySearchResponse?>
|
|
||||||
>
|
|
||||||
with
|
|
||||||
$FutureModifier<LibrarySearchResponse?>,
|
|
||||||
$FutureProvider<LibrarySearchResponse?> {
|
|
||||||
/// The provider for the search result.
|
|
||||||
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
|
static int finish(int hash) {
|
||||||
@override
|
// ignore: parameter_assignments
|
||||||
$FutureProviderElement<LibrarySearchResponse?> $createElement(
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
$ProviderPointer pointer,
|
// ignore: parameter_assignments
|
||||||
) => $FutureProviderElement(pointer);
|
hash = hash ^ (hash >> 11);
|
||||||
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The provider for the search result.
|
||||||
|
///
|
||||||
|
/// Copied from [searchResult].
|
||||||
|
@ProviderFor(searchResult)
|
||||||
|
const searchResultProvider = SearchResultFamily();
|
||||||
|
|
||||||
|
/// The provider for the search result.
|
||||||
|
///
|
||||||
|
/// Copied from [searchResult].
|
||||||
|
class SearchResultFamily extends Family<AsyncValue<LibrarySearchResponse?>> {
|
||||||
|
/// The provider for the search result.
|
||||||
|
///
|
||||||
|
/// Copied from [searchResult].
|
||||||
|
const SearchResultFamily();
|
||||||
|
|
||||||
|
/// The provider for the search result.
|
||||||
|
///
|
||||||
|
/// Copied from [searchResult].
|
||||||
|
SearchResultProvider call(
|
||||||
|
String query, {
|
||||||
|
int limit = 25,
|
||||||
|
}) {
|
||||||
|
return SearchResultProvider(
|
||||||
|
query,
|
||||||
|
limit: limit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<LibrarySearchResponse?> create(Ref ref) {
|
SearchResultProvider getProviderOverride(
|
||||||
final argument = this.argument as (String, {int limit});
|
covariant SearchResultProvider provider,
|
||||||
return searchResult(ref, argument.$1, limit: argument.limit);
|
) {
|
||||||
|
return call(
|
||||||
|
provider.query,
|
||||||
|
limit: provider.limit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'searchResultProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The provider for the search result.
|
||||||
|
///
|
||||||
|
/// Copied from [searchResult].
|
||||||
|
class SearchResultProvider
|
||||||
|
extends AutoDisposeFutureProvider<LibrarySearchResponse?> {
|
||||||
|
/// 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<LibrarySearchResponse?> 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<LibrarySearchResponse?> createElement() {
|
||||||
|
return _SearchResultProviderElement(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return other is SearchResultProvider && other.argument == argument;
|
return other is SearchResultProvider &&
|
||||||
|
other.query == query &&
|
||||||
|
other.limit == limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return argument.hashCode;
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, query.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, limit.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$searchResultHash() => r'33785de298ad0d53c9d21e8fec88ba2f22f1363f';
|
mixin SearchResultRef on AutoDisposeFutureProviderRef<LibrarySearchResponse?> {
|
||||||
|
/// The parameter `query` of this provider.
|
||||||
|
String get query;
|
||||||
|
|
||||||
/// The provider for the search result.
|
/// The parameter `limit` of this provider.
|
||||||
|
int get limit;
|
||||||
|
}
|
||||||
|
|
||||||
final class SearchResultFamily extends $Family
|
class _SearchResultProviderElement
|
||||||
with
|
extends AutoDisposeFutureProviderElement<LibrarySearchResponse?>
|
||||||
$FunctionalFamilyOverride<
|
with SearchResultRef {
|
||||||
FutureOr<LibrarySearchResponse?>,
|
_SearchResultProviderElement(super.provider);
|
||||||
(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
|
@override
|
||||||
String toString() => r'searchResultProvider';
|
String get query => (origin as SearchResultProvider).query;
|
||||||
|
@override
|
||||||
|
int get limit => (origin as SearchResultProvider).limit;
|
||||||
}
|
}
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,19 @@ class ExplorePage extends HookConsumerWidget {
|
||||||
final settings = ref.watch(appSettingsProvider);
|
final settings = ref.watch(appSettingsProvider);
|
||||||
final api = ref.watch(authenticatedApiProvider);
|
final api = ref.watch(authenticatedApiProvider);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Explore')),
|
appBar: AppBar(
|
||||||
|
title: const Text('Explore'),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
),
|
||||||
body: const MySearchBar(),
|
body: const MySearchBar(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MySearchBar extends HookConsumerWidget {
|
class MySearchBar extends HookConsumerWidget {
|
||||||
const MySearchBar({super.key});
|
const MySearchBar({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
|
@ -57,11 +62,8 @@ class MySearchBar extends HookConsumerWidget {
|
||||||
currentQuery = query;
|
currentQuery = query;
|
||||||
|
|
||||||
// In a real application, there should be some error handling here.
|
// In a real application, there should be some error handling here.
|
||||||
final options = await api.libraries.search(
|
final options = await api.libraries
|
||||||
libraryId: settings.activeLibraryId!,
|
.search(libraryId: settings.activeLibraryId!, query: query, limit: 3);
|
||||||
query: query,
|
|
||||||
limit: 3,
|
|
||||||
);
|
|
||||||
|
|
||||||
// If another search happened after this one, throw away these options.
|
// If another search happened after this one, throw away these options.
|
||||||
if (currentQuery != query) {
|
if (currentQuery != query) {
|
||||||
|
|
@ -96,9 +98,8 @@ class MySearchBar extends HookConsumerWidget {
|
||||||
// opacity: 0.5 for the hint text
|
// opacity: 0.5 for the hint text
|
||||||
hintStyle: WidgetStatePropertyAll(
|
hintStyle: WidgetStatePropertyAll(
|
||||||
Theme.of(context).textTheme.bodyMedium!.copyWith(
|
Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
color: Theme.of(
|
color:
|
||||||
context,
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.5),
|
||||||
).colorScheme.onSurface.withValues(alpha: 0.5),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
textInputAction: TextInputAction.search,
|
textInputAction: TextInputAction.search,
|
||||||
|
|
@ -118,7 +119,12 @@ class MySearchBar extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
viewOnSubmitted: (value) {
|
viewOnSubmitted: (value) {
|
||||||
context.pushNamed(Routes.search.name, queryParameters: {'q': value});
|
context.pushNamed(
|
||||||
|
Routes.search.name,
|
||||||
|
queryParameters: {
|
||||||
|
'q': value,
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
suggestionsBuilder: (context, controller) async {
|
suggestionsBuilder: (context, controller) async {
|
||||||
// check if the search controller is empty
|
// check if the search controller is empty
|
||||||
|
|
@ -184,12 +190,14 @@ List<Widget> buildBookSearchResult(
|
||||||
SearchResultMiniSection(
|
SearchResultMiniSection(
|
||||||
// title: 'Books',
|
// title: 'Books',
|
||||||
category: SearchResultCategory.books,
|
category: SearchResultCategory.books,
|
||||||
options: options.book.map((result) {
|
options: options.book.map(
|
||||||
|
(result) {
|
||||||
// convert result to a book object
|
// convert result to a book object
|
||||||
final book = result.libraryItem.media.asBookExpanded;
|
final book = result.libraryItem.media.asBookExpanded;
|
||||||
final metadata = book.metadata.asBookMetadataExpanded;
|
final metadata = book.metadata.asBookMetadataExpanded;
|
||||||
return BookSearchResultMini(book: book, metadata: metadata);
|
return BookSearchResultMini(book: book, metadata: metadata);
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -198,9 +206,11 @@ List<Widget> buildBookSearchResult(
|
||||||
SearchResultMiniSection(
|
SearchResultMiniSection(
|
||||||
// title: 'Authors',
|
// title: 'Authors',
|
||||||
category: SearchResultCategory.authors,
|
category: SearchResultCategory.authors,
|
||||||
options: options.authors.map((result) {
|
options: options.authors.map(
|
||||||
|
(result) {
|
||||||
return ListTile(title: Text(result.name));
|
return ListTile(title: Text(result.name));
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -221,7 +231,7 @@ class BookSearchResultMini extends HookConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final item = ref.watch(libraryItemProvider(book.libraryItemId)).value;
|
final item = ref.watch(libraryItemProvider(book.libraryItemId)).valueOrNull;
|
||||||
final image = item == null
|
final image = item == null
|
||||||
? const AsyncValue.loading()
|
? const AsyncValue.loading()
|
||||||
: ref.watch(coverImageProvider(item.id));
|
: ref.watch(coverImageProvider(item.id));
|
||||||
|
|
@ -234,7 +244,10 @@ class BookSearchResultMini extends HookConsumerWidget {
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
child: image.when(
|
child: image.when(
|
||||||
data: (bytes) => Image.memory(bytes, fit: BoxFit.cover),
|
data: (bytes) => Image.memory(
|
||||||
|
bytes,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
loading: () => const BookCoverSkeleton(),
|
loading: () => const BookCoverSkeleton(),
|
||||||
error: (error, _) => const Icon(Icons.error),
|
error: (error, _) => const Icon(Icons.error),
|
||||||
),
|
),
|
||||||
|
|
@ -245,7 +258,11 @@ class BookSearchResultMini extends HookConsumerWidget {
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
metadata.authors.map((author) => author.name).join(', '),
|
metadata.authors
|
||||||
|
.map(
|
||||||
|
(author) => author.name,
|
||||||
|
)
|
||||||
|
.join(', '),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// navigate to the book details page
|
// navigate to the book details page
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,13 @@ import 'package:vaani/features/explore/providers/search_result_provider.dart';
|
||||||
import 'package:vaani/features/explore/view/explore_page.dart';
|
import 'package:vaani/features/explore/view/explore_page.dart';
|
||||||
import 'package:vaani/shared/extensions/model_conversions.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 {
|
class SearchResultPage extends HookConsumerWidget {
|
||||||
const SearchResultPage({
|
const SearchResultPage({
|
||||||
|
|
@ -35,7 +41,9 @@ class SearchResultPage extends HookConsumerWidget {
|
||||||
body: results.when(
|
body: results.when(
|
||||||
data: (options) {
|
data: (options) {
|
||||||
if (options == null) {
|
if (options == null) {
|
||||||
return Container(child: const Text('No data found'));
|
return Container(
|
||||||
|
child: const Text('No data found'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (options is BookLibrarySearchResponse) {
|
if (options is BookLibrarySearchResponse) {
|
||||||
if (category == null) {
|
if (category == null) {
|
||||||
|
|
@ -49,7 +57,10 @@ class SearchResultPage extends HookConsumerWidget {
|
||||||
options.book[index].libraryItem.media.asBookExpanded;
|
options.book[index].libraryItem.media.asBookExpanded;
|
||||||
final metadata = book.metadata.asBookMetadataExpanded;
|
final metadata = book.metadata.asBookMetadataExpanded;
|
||||||
|
|
||||||
return BookSearchResultMini(book: book, metadata: metadata);
|
return BookSearchResultMini(
|
||||||
|
book: book,
|
||||||
|
metadata: metadata,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SearchResultCategory.authors => Container(),
|
SearchResultCategory.authors => Container(),
|
||||||
|
|
@ -60,8 +71,12 @@ class SearchResultPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
loading: () => const Center(child: CircularProgressIndicator()),
|
loading: () => const Center(
|
||||||
error: (error, stackTrace) => Center(child: Text('Error: $error')),
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
error: (error, stackTrace) => Center(
|
||||||
|
child: Text('Error: $error'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,16 @@ import 'package:vaani/shared/extensions/model_conversions.dart';
|
||||||
import 'package:vaani/shared/utils.dart';
|
import 'package:vaani/shared/utils.dart';
|
||||||
|
|
||||||
class LibraryItemActions extends HookConsumerWidget {
|
class LibraryItemActions extends HookConsumerWidget {
|
||||||
const LibraryItemActions({super.key, required this.id});
|
const LibraryItemActions({
|
||||||
|
super.key,
|
||||||
|
required this.id,
|
||||||
|
});
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final item = ref.watch(libraryItemProvider(id)).value;
|
final item = ref.watch(libraryItemProvider(id)).valueOrNull;
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +68,9 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
// read list button
|
// read list button
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
icon: const Icon(Icons.playlist_add_rounded),
|
icon: const Icon(
|
||||||
|
Icons.playlist_add_rounded,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
// share button
|
// share button
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|
@ -74,9 +79,8 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
var currentServerUrl =
|
var currentServerUrl =
|
||||||
apiSettings.activeServer!.serverUrl;
|
apiSettings.activeServer!.serverUrl;
|
||||||
if (!currentServerUrl.hasScheme) {
|
if (!currentServerUrl.hasScheme) {
|
||||||
currentServerUrl = Uri.https(
|
currentServerUrl =
|
||||||
currentServerUrl.toString(),
|
Uri.https(currentServerUrl.toString());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
handleLaunchUrl(
|
handleLaunchUrl(
|
||||||
Uri.parse(
|
Uri.parse(
|
||||||
|
|
@ -136,8 +140,7 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
.database
|
.database
|
||||||
.deleteRecordWithId(
|
.deleteRecordWithId(
|
||||||
record
|
record
|
||||||
.task
|
.task.taskId,
|
||||||
.taskId,
|
|
||||||
);
|
);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
|
|
@ -179,13 +182,16 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
loading: () => const Center(
|
loading: () => const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
error: (error, stackTrace) =>
|
error: (error, stackTrace) => Center(
|
||||||
Center(child: Text('Error: $error')),
|
child: Text('Error: $error'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.more_vert_rounded),
|
icon: const Icon(
|
||||||
|
Icons.more_vert_rounded,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -200,20 +206,25 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class LibItemDownloadButton extends HookConsumerWidget {
|
class LibItemDownloadButton extends HookConsumerWidget {
|
||||||
const LibItemDownloadButton({super.key, required this.item});
|
const LibItemDownloadButton({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
final shelfsdk.LibraryItemExpanded item;
|
final shelfsdk.LibraryItemExpanded item;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final isItemDownloaded = ref.watch(isItemDownloadedProvider(item));
|
final isItemDownloaded = ref.watch(isItemDownloadedProvider(item));
|
||||||
if (isItemDownloaded.value ?? false) {
|
if (isItemDownloaded.valueOrNull ?? false) {
|
||||||
return AlreadyItemDownloadedButton(item: item);
|
return AlreadyItemDownloadedButton(item: item);
|
||||||
}
|
}
|
||||||
final isItemDownloading = ref.watch(isItemDownloadingProvider(item.id));
|
final isItemDownloading = ref.watch(isItemDownloadingProvider(item.id));
|
||||||
|
|
||||||
return isItemDownloading
|
return isItemDownloading
|
||||||
? ItemCurrentlyInDownloadQueue(item: item)
|
? ItemCurrentlyInDownloadQueue(
|
||||||
|
item: item,
|
||||||
|
)
|
||||||
: IconButton(
|
: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
appLogger.fine('Pressed download button');
|
appLogger.fine('Pressed download button');
|
||||||
|
|
@ -222,13 +233,18 @@ class LibItemDownloadButton extends HookConsumerWidget {
|
||||||
.read(downloadManagerProvider.notifier)
|
.read(downloadManagerProvider.notifier)
|
||||||
.queueAudioBookDownload(item);
|
.queueAudioBookDownload(item);
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.download_rounded),
|
icon: const Icon(
|
||||||
|
Icons.download_rounded,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ItemCurrentlyInDownloadQueue extends HookConsumerWidget {
|
class ItemCurrentlyInDownloadQueue extends HookConsumerWidget {
|
||||||
const ItemCurrentlyInDownloadQueue({super.key, required this.item});
|
const ItemCurrentlyInDownloadQueue({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
final shelfsdk.LibraryItemExpanded item;
|
final shelfsdk.LibraryItemExpanded item;
|
||||||
|
|
||||||
|
|
@ -236,7 +252,7 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final progress = ref
|
final progress = ref
|
||||||
.watch(itemDownloadProgressProvider(item.id))
|
.watch(itemDownloadProgressProvider(item.id))
|
||||||
.value
|
.valueOrNull
|
||||||
?.clamp(0.05, 1.0);
|
?.clamp(0.05, 1.0);
|
||||||
|
|
||||||
if (progress == 1) {
|
if (progress == 1) {
|
||||||
|
|
@ -247,12 +263,17 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget {
|
||||||
return Stack(
|
return Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
CircularProgressIndicator(value: progress, strokeWidth: 2),
|
CircularProgressIndicator(
|
||||||
|
value: progress,
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
const Icon(
|
const Icon(
|
||||||
Icons.download,
|
Icons.download,
|
||||||
// color: Theme.of(context).progressIndicatorTheme.color,
|
// color: Theme.of(context).progressIndicatorTheme.color,
|
||||||
)
|
)
|
||||||
.animate(onPlay: (controller) => controller.repeat())
|
.animate(
|
||||||
|
onPlay: (controller) => controller.repeat(),
|
||||||
|
)
|
||||||
.fade(
|
.fade(
|
||||||
duration: shimmerDuration,
|
duration: shimmerDuration,
|
||||||
end: 1,
|
end: 1,
|
||||||
|
|
@ -271,7 +292,10 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AlreadyItemDownloadedButton extends HookConsumerWidget {
|
class AlreadyItemDownloadedButton extends HookConsumerWidget {
|
||||||
const AlreadyItemDownloadedButton({super.key, required this.item});
|
const AlreadyItemDownloadedButton({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
final shelfsdk.LibraryItemExpanded item;
|
final shelfsdk.LibraryItemExpanded item;
|
||||||
|
|
||||||
|
|
@ -293,18 +317,25 @@ class AlreadyItemDownloadedButton extends HookConsumerWidget {
|
||||||
top: 8.0,
|
top: 8.0,
|
||||||
bottom: (isBookPlaying ? playerMinHeight : 0) + 8,
|
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 {
|
class DownloadSheet extends HookConsumerWidget {
|
||||||
const DownloadSheet({super.key, required this.item});
|
const DownloadSheet({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
final shelfsdk.LibraryItemExpanded item;
|
final shelfsdk.LibraryItemExpanded item;
|
||||||
|
|
||||||
|
|
@ -336,7 +367,9 @@ class DownloadSheet extends HookConsumerWidget {
|
||||||
// ),
|
// ),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Delete'),
|
title: const Text('Delete'),
|
||||||
leading: const Icon(Icons.delete_rounded),
|
leading: const Icon(
|
||||||
|
Icons.delete_rounded,
|
||||||
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
// show the delete dialog
|
// show the delete dialog
|
||||||
final wasDeleted = await showDialog<bool>(
|
final wasDeleted = await showDialog<bool>(
|
||||||
|
|
@ -354,7 +387,9 @@ class DownloadSheet extends HookConsumerWidget {
|
||||||
// delete the file
|
// delete the file
|
||||||
ref
|
ref
|
||||||
.read(downloadManagerProvider.notifier)
|
.read(downloadManagerProvider.notifier)
|
||||||
.deleteDownloadedItem(item);
|
.deleteDownloadedItem(
|
||||||
|
item,
|
||||||
|
);
|
||||||
GoRouter.of(context).pop(true);
|
GoRouter.of(context).pop(true);
|
||||||
},
|
},
|
||||||
child: const Text('Yes'),
|
child: const Text('Yes'),
|
||||||
|
|
@ -374,7 +409,11 @@ class DownloadSheet extends HookConsumerWidget {
|
||||||
appLogger.fine('Deleted ${item.media.metadata.title}');
|
appLogger.fine('Deleted ${item.media.metadata.title}');
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('Deleted ${item.media.metadata.title}')),
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'Deleted ${item.media.metadata.title}',
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -385,7 +424,10 @@ class DownloadSheet extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LibraryItemPlayButton extends HookConsumerWidget {
|
class _LibraryItemPlayButton extends HookConsumerWidget {
|
||||||
const _LibraryItemPlayButton({required this.item});
|
const _LibraryItemPlayButton({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
final shelfsdk.LibraryItemExpanded item;
|
final shelfsdk.LibraryItemExpanded item;
|
||||||
|
|
||||||
|
|
@ -436,7 +478,9 @@ class _LibraryItemPlayButton extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
label: Text(getPlayDisplayText()),
|
label: Text(getPlayDisplayText()),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -474,7 +518,7 @@ Future<void> libraryItemPlayButtonOnPressed({
|
||||||
required shelfsdk.BookExpanded book,
|
required shelfsdk.BookExpanded book,
|
||||||
shelfsdk.MediaProgress? userMediaProgress,
|
shelfsdk.MediaProgress? userMediaProgress,
|
||||||
}) async {
|
}) async {
|
||||||
appLogger.info('Pressed play/resume button');
|
debugPrint('Pressed play/resume button');
|
||||||
final player = ref.watch(audiobookPlayerProvider);
|
final player = ref.watch(audiobookPlayerProvider);
|
||||||
|
|
||||||
final isCurrentBookSetInPlayer = player.book == book;
|
final isCurrentBookSetInPlayer = player.book == book;
|
||||||
|
|
@ -483,12 +527,11 @@ Future<void> libraryItemPlayButtonOnPressed({
|
||||||
Future<void>? setSourceFuture;
|
Future<void>? setSourceFuture;
|
||||||
// set the book to the player if not already set
|
// set the book to the player if not already set
|
||||||
if (!isCurrentBookSetInPlayer) {
|
if (!isCurrentBookSetInPlayer) {
|
||||||
appLogger.info('Setting the book ${book.libraryItemId}');
|
debugPrint('Setting the book ${book.libraryItemId}');
|
||||||
appLogger.info('Initial position: ${userMediaProgress?.currentTime}');
|
debugPrint('Initial position: ${userMediaProgress?.currentTime}');
|
||||||
final downloadManager = ref.watch(simpleDownloadManagerProvider);
|
final downloadManager = ref.watch(simpleDownloadManagerProvider);
|
||||||
final libItem = await ref.read(
|
final libItem =
|
||||||
libraryItemProvider(book.libraryItemId).future,
|
await ref.read(libraryItemProvider(book.libraryItemId).future);
|
||||||
);
|
|
||||||
final downloadedUris = await downloadManager.getDownloadedFilesUri(libItem);
|
final downloadedUris = await downloadManager.getDownloadedFilesUri(libItem);
|
||||||
setSourceFuture = player.setSourceAudiobook(
|
setSourceFuture = player.setSourceAudiobook(
|
||||||
book,
|
book,
|
||||||
|
|
@ -496,17 +539,16 @@ Future<void> libraryItemPlayButtonOnPressed({
|
||||||
downloadedUris: downloadedUris,
|
downloadedUris: downloadedUris,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
appLogger.info('Book was already set');
|
debugPrint('Book was already set');
|
||||||
if (isPlayingThisBook) {
|
if (isPlayingThisBook) {
|
||||||
appLogger.info('Pausing the book');
|
debugPrint('Pausing the book');
|
||||||
await player.pause();
|
await player.pause();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set the volume as this is the first time playing and dismissing causes the volume to go to 0
|
// set the volume as this is the first time playing and dismissing causes the volume to go to 0
|
||||||
var bookPlayerSettings = ref
|
var bookPlayerSettings =
|
||||||
.read(bookSettingsProvider(book.libraryItemId))
|
ref.read(bookSettingsProvider(book.libraryItemId)).playerSettings;
|
||||||
.playerSettings;
|
|
||||||
var appPlayerSettings = ref.read(appSettingsProvider).playerSettings;
|
var appPlayerSettings = ref.read(appSettingsProvider).playerSettings;
|
||||||
|
|
||||||
var configurePlayerForEveryBook =
|
var configurePlayerForEveryBook =
|
||||||
|
|
|
||||||
|
|
@ -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/duration_format.dart';
|
||||||
import 'package:vaani/shared/extensions/model_conversions.dart';
|
import 'package:vaani/shared/extensions/model_conversions.dart';
|
||||||
import 'package:vaani/shared/widgets/shelves/book_shelf.dart';
|
import 'package:vaani/shared/widgets/shelves/book_shelf.dart';
|
||||||
import 'package:vaani/theme/providers/theme_from_cover_provider.dart';
|
import 'package:vaani/theme/theme_from_cover_provider.dart';
|
||||||
|
|
||||||
class LibraryItemHeroSection extends HookConsumerWidget {
|
class LibraryItemHeroSection extends HookConsumerWidget {
|
||||||
const LibraryItemHeroSection({
|
const LibraryItemHeroSection({
|
||||||
|
|
@ -42,13 +42,14 @@ class LibraryItemHeroSection extends HookConsumerWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag:
|
tag: HeroTagPrefixes.bookCover +
|
||||||
HeroTagPrefixes.bookCover +
|
|
||||||
itemId +
|
itemId +
|
||||||
(extraMap?.heroTagSuffix ?? ''),
|
(extraMap?.heroTagSuffix ?? ''),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: _BookCover(itemId: itemId),
|
child: _BookCover(
|
||||||
|
itemId: itemId,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// a progress bar
|
// a progress bar
|
||||||
|
|
@ -58,7 +59,9 @@ class LibraryItemHeroSection extends HookConsumerWidget {
|
||||||
right: 8.0,
|
right: 8.0,
|
||||||
left: 8.0,
|
left: 8.0,
|
||||||
),
|
),
|
||||||
child: _LibraryItemProgressIndicator(id: itemId),
|
child: _LibraryItemProgressIndicator(
|
||||||
|
id: itemId,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -74,7 +77,11 @@ class LibraryItemHeroSection extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BookDetails extends HookConsumerWidget {
|
class _BookDetails extends HookConsumerWidget {
|
||||||
const _BookDetails({required this.id, this.extraMap});
|
const _BookDetails({
|
||||||
|
super.key,
|
||||||
|
required this.id,
|
||||||
|
this.extraMap,
|
||||||
|
});
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
final LibraryItemExtras? extraMap;
|
final LibraryItemExtras? extraMap;
|
||||||
|
|
@ -84,7 +91,7 @@ class _BookDetails extends HookConsumerWidget {
|
||||||
final itemFromApi = ref.watch(libraryItemProvider(id));
|
final itemFromApi = ref.watch(libraryItemProvider(id));
|
||||||
|
|
||||||
final itemBookMetadata =
|
final itemBookMetadata =
|
||||||
itemFromApi.value?.media.metadata.asBookMetadataExpanded;
|
itemFromApi.valueOrNull?.media.metadata.asBookMetadataExpanded;
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -93,7 +100,10 @@ class _BookDetails extends HookConsumerWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_BookTitle(extraMap: extraMap, itemBookMetadata: itemBookMetadata),
|
_BookTitle(
|
||||||
|
extraMap: extraMap,
|
||||||
|
itemBookMetadata: itemBookMetadata,
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.symmetric(vertical: 16),
|
margin: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
@ -125,14 +135,17 @@ class _BookDetails extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LibraryItemProgressIndicator extends HookConsumerWidget {
|
class _LibraryItemProgressIndicator extends HookConsumerWidget {
|
||||||
const _LibraryItemProgressIndicator({required this.id});
|
const _LibraryItemProgressIndicator({
|
||||||
|
super.key,
|
||||||
|
required this.id,
|
||||||
|
});
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final player = ref.watch(audiobookPlayerProvider);
|
final player = ref.watch(audiobookPlayerProvider);
|
||||||
final libraryItem = ref.watch(libraryItemProvider(id)).value;
|
final libraryItem = ref.watch(libraryItemProvider(id)).valueOrNull;
|
||||||
if (libraryItem == null) {
|
if (libraryItem == null) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
@ -146,15 +159,13 @@ class _LibraryItemProgressIndicator extends HookConsumerWidget {
|
||||||
Duration remainingTime;
|
Duration remainingTime;
|
||||||
if (player.book?.libraryItemId == libraryItem.id) {
|
if (player.book?.libraryItemId == libraryItem.id) {
|
||||||
// final positionStream = useStream(player.slowPositionStream);
|
// final positionStream = useStream(player.slowPositionStream);
|
||||||
progress =
|
progress = (player.positionInBook).inSeconds /
|
||||||
(player.positionInBook).inSeconds /
|
|
||||||
libraryItem.media.asBookExpanded.duration.inSeconds;
|
libraryItem.media.asBookExpanded.duration.inSeconds;
|
||||||
remainingTime =
|
remainingTime =
|
||||||
libraryItem.media.asBookExpanded.duration - player.positionInBook;
|
libraryItem.media.asBookExpanded.duration - player.positionInBook;
|
||||||
} else {
|
} else {
|
||||||
progress = mediaProgress?.progress ?? 0;
|
progress = mediaProgress?.progress ?? 0;
|
||||||
remainingTime =
|
remainingTime = (libraryItem.media.asBookExpanded.duration -
|
||||||
(libraryItem.media.asBookExpanded.duration -
|
|
||||||
mediaProgress!.currentTime);
|
mediaProgress!.currentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,16 +192,17 @@ class _LibraryItemProgressIndicator extends HookConsumerWidget {
|
||||||
semanticsLabel: 'Book progress',
|
semanticsLabel: 'Book progress',
|
||||||
semanticsValue: '${progressInPercent.toStringAsFixed(2)}%',
|
semanticsValue: '${progressInPercent.toStringAsFixed(2)}%',
|
||||||
),
|
),
|
||||||
const SizedBox.square(dimension: 4.0),
|
const SizedBox.square(
|
||||||
|
dimension: 4.0,
|
||||||
|
),
|
||||||
// time remaining
|
// time remaining
|
||||||
Text(
|
Text(
|
||||||
// only show 2 decimal places
|
// only show 2 decimal places
|
||||||
'${remainingTime.smartBinaryFormat} left',
|
'${remainingTime.smartBinaryFormat} left',
|
||||||
|
|
||||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
color: Theme.of(
|
color:
|
||||||
context,
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
|
||||||
).colorScheme.onSurface.withValues(alpha: 0.75),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -200,7 +212,11 @@ class _LibraryItemProgressIndicator extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HeroSectionSubLabelWithIcon extends HookConsumerWidget {
|
class _HeroSectionSubLabelWithIcon extends HookConsumerWidget {
|
||||||
const _HeroSectionSubLabelWithIcon({required this.icon, required this.text});
|
const _HeroSectionSubLabelWithIcon({
|
||||||
|
super.key,
|
||||||
|
required this.icon,
|
||||||
|
required this.text,
|
||||||
|
});
|
||||||
|
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final Widget text;
|
final Widget text;
|
||||||
|
|
@ -210,13 +226,11 @@ class _HeroSectionSubLabelWithIcon extends HookConsumerWidget {
|
||||||
final themeData = Theme.of(context);
|
final themeData = Theme.of(context);
|
||||||
final useFontAwesome =
|
final useFontAwesome =
|
||||||
icon.runtimeType == FontAwesomeIcons.book.runtimeType;
|
icon.runtimeType == FontAwesomeIcons.book.runtimeType;
|
||||||
final useMaterialThemeOnItemPage = ref
|
final useMaterialThemeOnItemPage =
|
||||||
.watch(appSettingsProvider)
|
ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage;
|
||||||
.themeSettings
|
|
||||||
.useMaterialThemeOnItemPage;
|
|
||||||
final color = useMaterialThemeOnItemPage
|
final color = useMaterialThemeOnItemPage
|
||||||
? themeData.colorScheme.primary
|
? themeData.colorScheme.primary
|
||||||
: themeData.colorScheme.onSurface.withValues(alpha: 0.75);
|
: themeData.colorScheme.onSurface.withOpacity(0.75);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
@ -224,10 +238,20 @@ class _HeroSectionSubLabelWithIcon extends HookConsumerWidget {
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(right: 8, top: 2),
|
margin: const EdgeInsets.only(right: 8, top: 2),
|
||||||
child: useFontAwesome
|
child: useFontAwesome
|
||||||
? FaIcon(icon, size: 16, color: color)
|
? FaIcon(
|
||||||
: Icon(icon, size: 16, color: color),
|
icon,
|
||||||
|
size: 16,
|
||||||
|
color: color,
|
||||||
|
)
|
||||||
|
: Icon(
|
||||||
|
icon,
|
||||||
|
size: 16,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: text,
|
||||||
),
|
),
|
||||||
Expanded(child: text),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -236,6 +260,7 @@ class _HeroSectionSubLabelWithIcon extends HookConsumerWidget {
|
||||||
|
|
||||||
class _BookSeries extends StatelessWidget {
|
class _BookSeries extends StatelessWidget {
|
||||||
const _BookSeries({
|
const _BookSeries({
|
||||||
|
super.key,
|
||||||
required this.itemBookMetadata,
|
required this.itemBookMetadata,
|
||||||
required this.bookDetailsCached,
|
required this.bookDetailsCached,
|
||||||
});
|
});
|
||||||
|
|
@ -281,6 +306,7 @@ class _BookSeries extends StatelessWidget {
|
||||||
|
|
||||||
class _BookNarrators extends StatelessWidget {
|
class _BookNarrators extends StatelessWidget {
|
||||||
const _BookNarrators({
|
const _BookNarrators({
|
||||||
|
super.key,
|
||||||
required this.itemBookMetadata,
|
required this.itemBookMetadata,
|
||||||
required this.bookDetailsCached,
|
required this.bookDetailsCached,
|
||||||
});
|
});
|
||||||
|
|
@ -315,7 +341,10 @@ class _BookNarrators extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BookCover extends HookConsumerWidget {
|
class _BookCover extends HookConsumerWidget {
|
||||||
const _BookCover({required this.itemId});
|
const _BookCover({
|
||||||
|
super.key,
|
||||||
|
required this.itemId,
|
||||||
|
});
|
||||||
|
|
||||||
final String itemId;
|
final String itemId;
|
||||||
|
|
||||||
|
|
@ -324,27 +353,25 @@ class _BookCover extends HookConsumerWidget {
|
||||||
final coverImage = ref.watch(coverImageProvider(itemId));
|
final coverImage = ref.watch(coverImageProvider(itemId));
|
||||||
final themeData = Theme.of(context);
|
final themeData = Theme.of(context);
|
||||||
// final item = ref.watch(libraryItemProvider(itemId));
|
// final item = ref.watch(libraryItemProvider(itemId));
|
||||||
final themeSettings = ref.watch(appSettingsProvider).themeSettings;
|
final useMaterialThemeOnItemPage =
|
||||||
|
ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage;
|
||||||
|
|
||||||
ColorScheme? coverColorScheme;
|
ColorScheme? coverColorScheme;
|
||||||
if (themeSettings.useMaterialThemeOnItemPage) {
|
if (useMaterialThemeOnItemPage) {
|
||||||
coverColorScheme = ref
|
coverColorScheme = ref
|
||||||
.watch(
|
.watch(
|
||||||
themeOfLibraryItemProvider(
|
themeOfLibraryItemProvider(
|
||||||
itemId,
|
itemId,
|
||||||
brightness: Theme.of(context).brightness,
|
brightness: Theme.of(context).brightness,
|
||||||
highContrast:
|
|
||||||
themeSettings.highContrast ||
|
|
||||||
MediaQuery.of(context).highContrast,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.value;
|
.valueOrNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ThemeSwitcher(
|
return ThemeSwitcher(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
// change theme after 2 seconds
|
// change theme after 2 seconds
|
||||||
if (themeSettings.useMaterialThemeOnItemPage) {
|
if (useMaterialThemeOnItemPage) {
|
||||||
Future.delayed(150.ms, () {
|
Future.delayed(150.ms, () {
|
||||||
try {
|
try {
|
||||||
ThemeSwitcher.of(context).changeTheme(
|
ThemeSwitcher.of(context).changeTheme(
|
||||||
|
|
@ -356,7 +383,7 @@ class _BookCover extends HookConsumerWidget {
|
||||||
: themeData,
|
: themeData,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
appLogger.severe('Error changing theme: $e');
|
appLogger.shout('Error changing theme: $e');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -367,10 +394,15 @@ class _BookCover extends HookConsumerWidget {
|
||||||
return const Icon(Icons.error);
|
return const Icon(Icons.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Image.memory(image, fit: BoxFit.cover);
|
return Image.memory(
|
||||||
|
image,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
loading: () {
|
loading: () {
|
||||||
return const Center(child: BookCoverSkeleton());
|
return const Center(
|
||||||
|
child: BookCoverSkeleton(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
error: (error, stack) {
|
error: (error, stack) {
|
||||||
return const Center(child: Icon(Icons.error));
|
return const Center(child: Icon(Icons.error));
|
||||||
|
|
@ -382,7 +414,11 @@ class _BookCover extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BookTitle extends StatelessWidget {
|
class _BookTitle extends StatelessWidget {
|
||||||
const _BookTitle({required this.extraMap, required this.itemBookMetadata});
|
const _BookTitle({
|
||||||
|
super.key,
|
||||||
|
required this.extraMap,
|
||||||
|
required this.itemBookMetadata,
|
||||||
|
});
|
||||||
|
|
||||||
final LibraryItemExtras? extraMap;
|
final LibraryItemExtras? extraMap;
|
||||||
final shelfsdk.BookMetadataExpanded? itemBookMetadata;
|
final shelfsdk.BookMetadataExpanded? itemBookMetadata;
|
||||||
|
|
@ -394,8 +430,7 @@ class _BookTitle extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag:
|
tag: HeroTagPrefixes.bookTitle +
|
||||||
HeroTagPrefixes.bookTitle +
|
|
||||||
// itemId +
|
// itemId +
|
||||||
(extraMap?.heroTagSuffix ?? ''),
|
(extraMap?.heroTagSuffix ?? ''),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
@ -414,7 +449,7 @@ class _BookTitle extends StatelessWidget {
|
||||||
? const SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: Text(
|
: Text(
|
||||||
style: themeData.textTheme.titleSmall?.copyWith(
|
style: themeData.textTheme.titleSmall?.copyWith(
|
||||||
color: themeData.colorScheme.onSurface.withValues(alpha: 0.8),
|
color: themeData.colorScheme.onSurface.withOpacity(0.8),
|
||||||
),
|
),
|
||||||
itemBookMetadata?.subtitle ?? '',
|
itemBookMetadata?.subtitle ?? '',
|
||||||
),
|
),
|
||||||
|
|
@ -425,6 +460,7 @@ class _BookTitle extends StatelessWidget {
|
||||||
|
|
||||||
class _BookAuthors extends StatelessWidget {
|
class _BookAuthors extends StatelessWidget {
|
||||||
const _BookAuthors({
|
const _BookAuthors({
|
||||||
|
super.key,
|
||||||
required this.itemBookMetadata,
|
required this.itemBookMetadata,
|
||||||
required this.bookDetailsCached,
|
required this.bookDetailsCached,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,16 @@ import 'package:vaani/api/library_item_provider.dart';
|
||||||
import 'package:vaani/shared/extensions/model_conversions.dart';
|
import 'package:vaani/shared/extensions/model_conversions.dart';
|
||||||
|
|
||||||
class LibraryItemMetadata extends HookConsumerWidget {
|
class LibraryItemMetadata extends HookConsumerWidget {
|
||||||
const LibraryItemMetadata({super.key, required this.id});
|
const LibraryItemMetadata({
|
||||||
|
super.key,
|
||||||
|
required this.id,
|
||||||
|
});
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final item = ref.watch(libraryItemProvider(id)).value;
|
final item = ref.watch(libraryItemProvider(id)).valueOrNull;
|
||||||
|
|
||||||
/// formats the duration of the book as `10h 30m`
|
/// formats the duration of the book as `10h 30m`
|
||||||
///
|
///
|
||||||
|
|
@ -69,8 +72,7 @@ class LibraryItemMetadata extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
_MetadataItem(
|
_MetadataItem(
|
||||||
title: 'Published',
|
title: 'Published',
|
||||||
value:
|
value: itemBookMetadata?.publishedDate ??
|
||||||
itemBookMetadata?.publishedDate ??
|
|
||||||
itemBookMetadata?.publishedYear ??
|
itemBookMetadata?.publishedYear ??
|
||||||
'Unknown',
|
'Unknown',
|
||||||
),
|
),
|
||||||
|
|
@ -85,18 +87,19 @@ class LibraryItemMetadata extends HookConsumerWidget {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
// alternate between metadata and vertical divider
|
// alternate between metadata and vertical divider
|
||||||
children: List.generate(children.length * 2 - 1, (index) {
|
children: List.generate(
|
||||||
|
children.length * 2 - 1,
|
||||||
|
(index) {
|
||||||
if (index.isEven) {
|
if (index.isEven) {
|
||||||
return children[index ~/ 2];
|
return children[index ~/ 2];
|
||||||
}
|
}
|
||||||
return VerticalDivider(
|
return VerticalDivider(
|
||||||
indent: 6,
|
indent: 6,
|
||||||
endIndent: 6,
|
endIndent: 6,
|
||||||
color: Theme.of(
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||||
context,
|
|
||||||
).colorScheme.onSurface.withValues(alpha: 0.6),
|
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -105,7 +108,11 @@ class LibraryItemMetadata extends HookConsumerWidget {
|
||||||
|
|
||||||
/// key-value pair to display as column
|
/// key-value pair to display as column
|
||||||
class _MetadataItem extends StatelessWidget {
|
class _MetadataItem extends StatelessWidget {
|
||||||
const _MetadataItem({required this.title, required this.value});
|
const _MetadataItem({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.value,
|
||||||
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final String value;
|
final String value;
|
||||||
|
|
@ -119,7 +126,7 @@ class _MetadataItem extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
style: themeData.textTheme.titleMedium?.copyWith(
|
style: themeData.textTheme.titleMedium?.copyWith(
|
||||||
color: themeData.colorScheme.onSurface.withValues(alpha: 0.90),
|
color: themeData.colorScheme.onSurface.withOpacity(0.90),
|
||||||
),
|
),
|
||||||
value,
|
value,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|
@ -127,7 +134,7 @@ class _MetadataItem extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
style: themeData.textTheme.bodySmall?.copyWith(
|
style: themeData.textTheme.bodySmall?.copyWith(
|
||||||
color: themeData.colorScheme.onSurface.withValues(alpha: 0.7),
|
color: themeData.colorScheme.onSurface.withOpacity(0.7),
|
||||||
),
|
),
|
||||||
title,
|
title,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,10 @@ import 'dart:math';
|
||||||
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:vaani/api/library_item_provider.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/item_viewer/view/library_item_sliver_app_bar.dart';
|
||||||
import 'package:vaani/features/player/view/mini_player_bottom_padding.dart';
|
import 'package:vaani/features/player/providers/player_form.dart';
|
||||||
import 'package:vaani/router/models/library_item_extras.dart';
|
import 'package:vaani/router/models/library_item_extras.dart';
|
||||||
import 'package:vaani/shared/widgets/expandable_description.dart';
|
import 'package:vaani/shared/widgets/expandable_description.dart';
|
||||||
|
|
||||||
|
|
@ -16,86 +15,27 @@ import 'library_item_hero_section.dart';
|
||||||
import 'library_item_metadata.dart';
|
import 'library_item_metadata.dart';
|
||||||
|
|
||||||
class LibraryItemPage extends HookConsumerWidget {
|
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 String itemId;
|
||||||
final Object? extra;
|
final Object? extra;
|
||||||
static const double _showFabThreshold = 300.0;
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final additionalItemData = extra is LibraryItemExtras
|
final additionalItemData =
|
||||||
? extra as LibraryItemExtras
|
extra is LibraryItemExtras ? extra as LibraryItemExtras : null;
|
||||||
: 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(
|
return ThemeProvider(
|
||||||
initTheme: Theme.of(context),
|
initTheme: Theme.of(context),
|
||||||
duration: 200.ms,
|
duration: 200.ms,
|
||||||
child: ThemeSwitchingArea(
|
child: ThemeSwitchingArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
floatingActionButton: AnimatedSwitcher(
|
|
||||||
duration: 250.ms,
|
|
||||||
// A common transition for FABs (fade + scale)
|
|
||||||
transitionBuilder: (Widget child, Animation<double> 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(
|
body: CustomScrollView(
|
||||||
controller: scrollController,
|
|
||||||
slivers: [
|
slivers: [
|
||||||
LibraryItemSliverAppBar(
|
const LibraryItemSliverAppBar(),
|
||||||
id: itemId,
|
|
||||||
scrollController: scrollController,
|
|
||||||
),
|
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
sliver: LibraryItemHeroSection(
|
sliver: LibraryItemHeroSection(
|
||||||
|
|
@ -104,13 +44,21 @@ class LibraryItemPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// a horizontal display with dividers of metadata
|
// 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
|
// 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
|
// 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
|
// a padding at the bottom to make sure the last item is not hidden by mini player
|
||||||
const SliverToBoxAdapter(child: MiniPlayerBottomPadding()),
|
const SliverToBoxAdapter(
|
||||||
|
child: SizedBox(height: playerMinHeight),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -120,12 +68,15 @@ class LibraryItemPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class LibraryItemDescription extends HookConsumerWidget {
|
class LibraryItemDescription extends HookConsumerWidget {
|
||||||
const LibraryItemDescription({super.key, required this.id});
|
const LibraryItemDescription({
|
||||||
|
super.key,
|
||||||
|
required this.id,
|
||||||
|
});
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final item = ref.watch(libraryItemProvider(id)).value;
|
final item = ref.watch(libraryItemProvider(id)).valueOrNull;
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
|
@ -140,21 +91,16 @@ class LibraryItemDescription extends HookConsumerWidget {
|
||||||
double calculateWidth(
|
double calculateWidth(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
BoxConstraints constraints, {
|
BoxConstraints constraints, {
|
||||||
|
|
||||||
/// width ratio of the cover image to the available width
|
/// width ratio of the cover image to the available width
|
||||||
double widthRatio = 0.4,
|
double widthRatio = 0.4,
|
||||||
|
|
||||||
/// height ratio of the cover image to the available height
|
/// height ratio of the cover image to the available height
|
||||||
double maxHeightToUse = 0.25,
|
double maxHeightToUse = 0.25,
|
||||||
}) {
|
}) {
|
||||||
final availHeight = min(
|
final availHeight =
|
||||||
constraints.maxHeight,
|
min(constraints.maxHeight, MediaQuery.of(context).size.height);
|
||||||
MediaQuery.of(context).size.height,
|
final availWidth =
|
||||||
);
|
min(constraints.maxWidth, MediaQuery.of(context).size.width);
|
||||||
final availWidth = min(
|
|
||||||
constraints.maxWidth,
|
|
||||||
MediaQuery.of(context).size.width,
|
|
||||||
);
|
|
||||||
|
|
||||||
// make the width widthRatio of the available width
|
// make the width widthRatio of the available width
|
||||||
var width = availWidth * widthRatio;
|
var width = availWidth * widthRatio;
|
||||||
|
|
|
||||||
|
|
@ -1,78 +1,23 @@
|
||||||
import 'package:flutter/material.dart';
|
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 HookConsumerWidget {
|
class LibraryItemSliverAppBar extends StatelessWidget {
|
||||||
const LibraryItemSliverAppBar({
|
const LibraryItemSliverAppBar({
|
||||||
super.key,
|
super.key,
|
||||||
required this.id,
|
|
||||||
required this.scrollController,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String id;
|
|
||||||
final ScrollController scrollController;
|
|
||||||
|
|
||||||
static const double _showTitleThreshold = kToolbarHeight * 0.5;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context) {
|
||||||
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(
|
return SliverAppBar(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
floating: false,
|
floating: true,
|
||||||
pinned: true,
|
|
||||||
primary: true,
|
primary: true,
|
||||||
|
snap: true,
|
||||||
actions: [
|
actions: [
|
||||||
// IconButton(
|
// cast button
|
||||||
// icon: const Icon(Icons.cast),
|
IconButton(onPressed: () {}, icon: const Icon(Icons.cast)),
|
||||||
// onPressed: () {
|
IconButton(onPressed: () {}, icon: const Icon(Icons.more_vert)),
|
||||||
// // 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,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,70 +1,38 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:vaani/api/library_provider.dart' show currentLibraryProvider;
|
import 'package:vaani/router/router.dart';
|
||||||
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 {
|
class LibraryBrowserPage extends HookConsumerWidget {
|
||||||
const LibraryBrowserPage({super.key});
|
const LibraryBrowserPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
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(
|
return Scaffold(
|
||||||
// Use CustomScrollView to enable slivers
|
appBar: AppBar(
|
||||||
body: CustomScrollView(
|
title: const Text('Library'),
|
||||||
slivers: <Widget>[
|
backgroundColor: Colors.transparent,
|
||||||
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),
|
// a list redirecting to authors, genres, and series pages
|
||||||
),
|
body: ListView(
|
||||||
SliverList(
|
children: [
|
||||||
delegate: SliverChildListDelegate([
|
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Authors'),
|
title: const Text('Authors'),
|
||||||
leading: const Icon(Icons.person),
|
leading: const Icon(Icons.person),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {},
|
||||||
showNotImplementedToast(context);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Genres'),
|
title: const Text('Genres'),
|
||||||
leading: const Icon(Icons.category),
|
leading: const Icon(Icons.category),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {},
|
||||||
showNotImplementedToast(context);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Series'),
|
title: const Text('Series'),
|
||||||
leading: const Icon(Icons.list),
|
leading: const Icon(Icons.list),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {},
|
||||||
showNotImplementedToast(context);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
// Downloads
|
// Downloads
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|
@ -75,8 +43,6 @@ class LibraryBrowserPage extends HookConsumerWidget {
|
||||||
GoRouter.of(context).pushNamed(Routes.downloads.name);
|
GoRouter.of(context).pushNamed(Routes.downloads.name);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
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<String> getLoggingFilePath() async {
|
|
||||||
final Directory directory = await getApplicationDocumentsDirectory();
|
|
||||||
return '${directory.path}/vaani.log';
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> 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}',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
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<List<LogRecord>> 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<void> clear() async {
|
|
||||||
final path = await getLoggingFilePath();
|
|
||||||
final file = File(path);
|
|
||||||
await file.writeAsString('');
|
|
||||||
state = AsyncData([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> 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<String> 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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
// 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<Logs, List<LogRecord>> {
|
|
||||||
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<List<LogRecord>> {
|
|
||||||
FutureOr<List<LogRecord>> build();
|
|
||||||
@$mustCallSuper
|
|
||||||
@override
|
|
||||||
void runBuild() {
|
|
||||||
final ref = this.ref as $Ref<AsyncValue<List<LogRecord>>, List<LogRecord>>;
|
|
||||||
final element =
|
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
|
||||||
AnyNotifier<AsyncValue<List<LogRecord>>, List<LogRecord>>,
|
|
||||||
AsyncValue<List<LogRecord>>,
|
|
||||||
Object?,
|
|
||||||
Object?
|
|
||||||
>;
|
|
||||||
element.handleCreate(ref, build);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,306 +0,0 @@
|
||||||
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<bool>(
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
part 'flow.freezed.dart';
|
part 'flow.freezed.dart';
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class Flow with _$Flow {
|
class Flow with _$Flow {
|
||||||
const factory Flow({
|
const factory Flow({
|
||||||
required Uri serverUri,
|
required Uri serverUri,
|
||||||
required String state,
|
required String state,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// 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
|
// 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,272 +9,241 @@ part of 'flow.dart';
|
||||||
// FreezedGenerator
|
// FreezedGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// dart format off
|
|
||||||
T _$identity<T>(T value) => value;
|
T _$identity<T>(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
|
/// @nodoc
|
||||||
mixin _$Flow {
|
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;
|
||||||
|
|
||||||
Uri get serverUri; String get state; String get verifier; Cookie get cookie; bool get isFlowComplete; String? get authToken;
|
|
||||||
/// Create a copy of Flow
|
/// Create a copy of Flow
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@pragma('vm:prefer-inline')
|
$FlowCopyWith<Flow> get copyWith => throw _privateConstructorUsedError;
|
||||||
$FlowCopyWith<Flow> get copyWith => _$FlowCopyWithImpl<Flow>(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
|
/// @nodoc
|
||||||
abstract mixin class $FlowCopyWith<$Res> {
|
abstract class $FlowCopyWith<$Res> {
|
||||||
factory $FlowCopyWith(Flow value, $Res Function(Flow) _then) = _$FlowCopyWithImpl;
|
factory $FlowCopyWith(Flow value, $Res Function(Flow) then) =
|
||||||
|
_$FlowCopyWithImpl<$Res, Flow>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call(
|
||||||
Uri serverUri, String state, String verifier, Cookie cookie, bool isFlowComplete, String? authToken
|
{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;
|
/// @nodoc
|
||||||
final $Res Function(Flow) _then;
|
class _$FlowCopyWithImpl<$Res, $Val extends Flow>
|
||||||
|
implements $FlowCopyWith<$Res> {
|
||||||
|
_$FlowCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
/// Create a copy of Flow
|
/// Create a copy of Flow
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// 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,}) {
|
@pragma('vm:prefer-inline')
|
||||||
return _then(_self.copyWith(
|
@override
|
||||||
serverUri: null == serverUri ? _self.serverUri : serverUri // ignore: cast_nullable_to_non_nullable
|
$Res call({
|
||||||
as Uri,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
|
Object? serverUri = null,
|
||||||
as String,verifier: null == verifier ? _self.verifier : verifier // ignore: cast_nullable_to_non_nullable
|
Object? state = null,
|
||||||
as String,cookie: null == cookie ? _self.cookie : cookie // ignore: cast_nullable_to_non_nullable
|
Object? verifier = null,
|
||||||
as Cookie,isFlowComplete: null == isFlowComplete ? _self.isFlowComplete : isFlowComplete // ignore: cast_nullable_to_non_nullable
|
Object? cookie = null,
|
||||||
as bool,authToken: freezed == authToken ? _self.authToken : authToken // ignore: cast_nullable_to_non_nullable
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @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?,
|
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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$FlowImpl implements _Flow {
|
||||||
class _Flow implements Flow {
|
const _$FlowImpl(
|
||||||
const _Flow({required this.serverUri, required this.state, required this.verifier, required this.cookie, this.isFlowComplete = false, this.authToken});
|
{required this.serverUri,
|
||||||
|
required this.state,
|
||||||
|
required this.verifier,
|
||||||
@override final Uri serverUri;
|
required this.cookie,
|
||||||
@override final String state;
|
this.isFlowComplete = false,
|
||||||
@override final String verifier;
|
this.authToken});
|
||||||
@override final Cookie cookie;
|
|
||||||
@override@JsonKey() final bool isFlowComplete;
|
|
||||||
@override final String? 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
|
@override
|
||||||
bool operator ==(Object other) {
|
final Uri serverUri;
|
||||||
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
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,serverUri,state,verifier,cookie,isFlowComplete,authToken);
|
final String state;
|
||||||
|
@override
|
||||||
|
final String verifier;
|
||||||
|
@override
|
||||||
|
final Cookie cookie;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isFlowComplete;
|
||||||
|
@override
|
||||||
|
final String? authToken;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'Flow(serverUri: $serverUri, state: $state, verifier: $verifier, cookie: $cookie, isFlowComplete: $isFlowComplete, authToken: $authToken)';
|
return 'Flow(serverUri: $serverUri, state: $state, verifier: $verifier, cookie: $cookie, isFlowComplete: $isFlowComplete, authToken: $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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
@override
|
||||||
abstract mixin class _$FlowCopyWith<$Res> implements $FlowCopyWith<$Res> {
|
int get hashCode => Object.hash(runtimeType, serverUri, state, verifier,
|
||||||
factory _$FlowCopyWith(_Flow value, $Res Function(_Flow) _then) = __$FlowCopyWithImpl;
|
cookie, isFlowComplete, authToken);
|
||||||
@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
|
/// Create a copy of Flow
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// 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,}) {
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
return _then(_Flow(
|
@override
|
||||||
serverUri: null == serverUri ? _self.serverUri : serverUri // ignore: cast_nullable_to_non_nullable
|
@pragma('vm:prefer-inline')
|
||||||
as Uri,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
|
_$$FlowImplCopyWith<_$FlowImpl> get copyWith =>
|
||||||
as String,verifier: null == verifier ? _self.verifier : verifier // ignore: cast_nullable_to_non_nullable
|
__$$FlowImplCopyWithImpl<_$FlowImpl>(this, _$identity);
|
||||||
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?,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dart format on
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:vaani/api/api_provider.dart';
|
import 'package:vaani/api/api_provider.dart';
|
||||||
import 'package:vaani/models/error_response.dart';
|
import 'package:vaani/models/error_response.dart';
|
||||||
|
|
@ -53,10 +52,8 @@ class OauthFlows extends _$OauthFlows {
|
||||||
}
|
}
|
||||||
state = {
|
state = {
|
||||||
...state,
|
...state,
|
||||||
oauthState: state[oauthState]!.copyWith(
|
oauthState: state[oauthState]!
|
||||||
isFlowComplete: true,
|
.copyWith(isFlowComplete: true, authToken: authToken),
|
||||||
authToken: authToken,
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +61,7 @@ class OauthFlows extends _$OauthFlows {
|
||||||
/// the code returned by the server in exchange for the verifier
|
/// the code returned by the server in exchange for the verifier
|
||||||
@riverpod
|
@riverpod
|
||||||
Future<String?> loginInExchangeForCode(
|
Future<String?> loginInExchangeForCode(
|
||||||
Ref ref, {
|
LoginInExchangeForCodeRef ref, {
|
||||||
required State oauthState,
|
required State oauthState,
|
||||||
required Code code,
|
required Code code,
|
||||||
ErrorResponseHandler? responseHandler,
|
ErrorResponseHandler? responseHandler,
|
||||||
|
|
|
||||||
|
|
@ -6,167 +6,219 @@ part of 'oauth_provider.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
String _$loginInExchangeForCodeHash() =>
|
||||||
// ignore_for_file: type=lint, type=warning
|
r'e931254959d9eb8196439c6b0c884c26cbe17c2f';
|
||||||
|
|
||||||
@ProviderFor(OauthFlows)
|
/// Copied from Dart SDK
|
||||||
final oauthFlowsProvider = OauthFlowsProvider._();
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
final class OauthFlowsProvider
|
static int combine(int hash, int value) {
|
||||||
extends $NotifierProvider<OauthFlows, Map<State, Flow>> {
|
// ignore: parameter_assignments
|
||||||
OauthFlowsProvider._()
|
hash = 0x1fffffff & (hash + value);
|
||||||
: super(
|
// ignore: parameter_assignments
|
||||||
from: null,
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
argument: null,
|
return hash ^ (hash >> 6);
|
||||||
retry: null,
|
|
||||||
name: r'oauthFlowsProvider',
|
|
||||||
isAutoDispose: false,
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$oauthFlowsHash();
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
OauthFlows create() => OauthFlows();
|
|
||||||
|
|
||||||
/// {@macro riverpod.override_with_value}
|
|
||||||
Override overrideWithValue(Map<State, Flow> value) {
|
|
||||||
return $ProviderOverride(
|
|
||||||
origin: this,
|
|
||||||
providerOverride: $SyncValueProvider<Map<State, Flow>>(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$oauthFlowsHash() => r'4e278baa0bf26f2a10694ca2caadb68dd5b6156f';
|
static int finish(int hash) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
abstract class _$OauthFlows extends $Notifier<Map<State, Flow>> {
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
Map<State, Flow> build();
|
// ignore: parameter_assignments
|
||||||
@$mustCallSuper
|
hash = hash ^ (hash >> 11);
|
||||||
@override
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
void runBuild() {
|
|
||||||
final ref = this.ref as $Ref<Map<State, Flow>, Map<State, Flow>>;
|
|
||||||
final element =
|
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
|
||||||
AnyNotifier<Map<State, Flow>, Map<State, Flow>>,
|
|
||||||
Map<State, Flow>,
|
|
||||||
Object?,
|
|
||||||
Object?
|
|
||||||
>;
|
|
||||||
element.handleCreate(ref, build);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the code returned by the server in exchange for the verifier
|
/// the code returned by the server in exchange for the verifier
|
||||||
|
///
|
||||||
|
/// Copied from [loginInExchangeForCode].
|
||||||
@ProviderFor(loginInExchangeForCode)
|
@ProviderFor(loginInExchangeForCode)
|
||||||
final loginInExchangeForCodeProvider = LoginInExchangeForCodeFamily._();
|
const loginInExchangeForCodeProvider = LoginInExchangeForCodeFamily();
|
||||||
|
|
||||||
/// the code returned by the server in exchange for the verifier
|
/// the code returned by the server in exchange for the verifier
|
||||||
|
///
|
||||||
final class LoginInExchangeForCodeProvider
|
/// Copied from [loginInExchangeForCode].
|
||||||
extends $FunctionalProvider<AsyncValue<String?>, String?, FutureOr<String?>>
|
class LoginInExchangeForCodeFamily extends Family<AsyncValue<String?>> {
|
||||||
with $FutureModifier<String?>, $FutureProvider<String?> {
|
|
||||||
/// the code returned by the server in exchange for the verifier
|
/// the code returned by the server in exchange for the verifier
|
||||||
LoginInExchangeForCodeProvider._({
|
///
|
||||||
required LoginInExchangeForCodeFamily super.from,
|
/// Copied from [loginInExchangeForCode].
|
||||||
required ({
|
const LoginInExchangeForCodeFamily();
|
||||||
State oauthState,
|
|
||||||
Code code,
|
/// the code returned by the server in exchange for the verifier
|
||||||
|
///
|
||||||
|
/// Copied from [loginInExchangeForCode].
|
||||||
|
LoginInExchangeForCodeProvider call({
|
||||||
|
required String oauthState,
|
||||||
|
required String code,
|
||||||
ErrorResponseHandler? responseHandler,
|
ErrorResponseHandler? responseHandler,
|
||||||
})
|
}) {
|
||||||
super.argument,
|
return LoginInExchangeForCodeProvider(
|
||||||
}) : super(
|
oauthState: oauthState,
|
||||||
retry: null,
|
code: code,
|
||||||
name: r'loginInExchangeForCodeProvider',
|
responseHandler: responseHandler,
|
||||||
isAutoDispose: true,
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$loginInExchangeForCodeHash();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return r'loginInExchangeForCodeProvider'
|
|
||||||
''
|
|
||||||
'$argument';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
@override
|
||||||
$FutureProviderElement<String?> $createElement($ProviderPointer pointer) =>
|
LoginInExchangeForCodeProvider getProviderOverride(
|
||||||
$FutureProviderElement(pointer);
|
covariant LoginInExchangeForCodeProvider provider,
|
||||||
|
) {
|
||||||
|
return call(
|
||||||
|
oauthState: provider.oauthState,
|
||||||
|
code: provider.code,
|
||||||
|
responseHandler: provider.responseHandler,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<String?> create(Ref ref) {
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
final argument =
|
|
||||||
this.argument
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
as ({
|
|
||||||
State oauthState,
|
@override
|
||||||
Code code,
|
Iterable<ProviderOrFamily>? 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<String?> {
|
||||||
|
/// the code returned by the server in exchange for the verifier
|
||||||
|
///
|
||||||
|
/// Copied from [loginInExchangeForCode].
|
||||||
|
LoginInExchangeForCodeProvider({
|
||||||
|
required String oauthState,
|
||||||
|
required String code,
|
||||||
ErrorResponseHandler? responseHandler,
|
ErrorResponseHandler? responseHandler,
|
||||||
});
|
}) : this._internal(
|
||||||
return loginInExchangeForCode(
|
(ref) => loginInExchangeForCode(
|
||||||
ref,
|
ref as LoginInExchangeForCodeRef,
|
||||||
oauthState: argument.oauthState,
|
oauthState: oauthState,
|
||||||
code: argument.code,
|
code: code,
|
||||||
responseHandler: argument.responseHandler,
|
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<String?> Function(LoginInExchangeForCodeRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: LoginInExchangeForCodeProvider._internal(
|
||||||
|
(ref) => create(ref as LoginInExchangeForCodeRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
oauthState: oauthState,
|
||||||
|
code: code,
|
||||||
|
responseHandler: responseHandler,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<String?> createElement() {
|
||||||
|
return _LoginInExchangeForCodeProviderElement(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return other is LoginInExchangeForCodeProvider &&
|
return other is LoginInExchangeForCodeProvider &&
|
||||||
other.argument == argument;
|
other.oauthState == oauthState &&
|
||||||
|
other.code == code &&
|
||||||
|
other.responseHandler == responseHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return argument.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$loginInExchangeForCodeHash() =>
|
mixin LoginInExchangeForCodeRef on AutoDisposeFutureProviderRef<String?> {
|
||||||
r'bfc3945529048a0f536052fd5579b76457560fcd';
|
/// The parameter `oauthState` of this provider.
|
||||||
|
String get oauthState;
|
||||||
|
|
||||||
/// the code returned by the server in exchange for the verifier
|
/// The parameter `code` of this provider.
|
||||||
|
String get code;
|
||||||
|
|
||||||
final class LoginInExchangeForCodeFamily extends $Family
|
/// The parameter `responseHandler` of this provider.
|
||||||
with
|
ErrorResponseHandler? get responseHandler;
|
||||||
$FunctionalFamilyOverride<
|
}
|
||||||
FutureOr<String?>,
|
|
||||||
({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
|
class _LoginInExchangeForCodeProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<String?>
|
||||||
LoginInExchangeForCodeProvider call({
|
with LoginInExchangeForCodeRef {
|
||||||
required State oauthState,
|
_LoginInExchangeForCodeProviderElement(super.provider);
|
||||||
required Code code,
|
|
||||||
ErrorResponseHandler? responseHandler,
|
|
||||||
}) => LoginInExchangeForCodeProvider._(
|
|
||||||
argument: (
|
|
||||||
oauthState: oauthState,
|
|
||||||
code: code,
|
|
||||||
responseHandler: responseHandler,
|
|
||||||
),
|
|
||||||
from: this,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => r'loginInExchangeForCodeProvider';
|
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<OauthFlows, Map<State, Flow>>.internal(
|
||||||
|
OauthFlows.new,
|
||||||
|
name: r'oauthFlowsProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product') ? null : _$oauthFlowsHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef _$OauthFlows = Notifier<Map<State, Flow>>;
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ class CallbackPage extends HookConsumerWidget {
|
||||||
|
|
||||||
// check if the state is in the flows
|
// check if the state is in the flows
|
||||||
if (!flows.containsKey(state)) {
|
if (!flows.containsKey(state)) {
|
||||||
return const _SomethingWentWrong(message: 'State not found');
|
return const _SomethingWentWrong(
|
||||||
|
message: 'State not found',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the token
|
// get the token
|
||||||
|
|
@ -43,21 +45,26 @@ class CallbackPage extends HookConsumerWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text('Contacting server...\nPlease wait\n\nGot:'
|
||||||
'Contacting server...\nPlease wait\n\nGot:'
|
'\nState: $state\nCode: $code'),
|
||||||
'\nState: $state\nCode: $code',
|
|
||||||
),
|
|
||||||
loginAuthToken.when(
|
loginAuthToken.when(
|
||||||
data: (authenticationToken) {
|
data: (authenticationToken) {
|
||||||
if (authenticationToken == null) {
|
if (authenticationToken == null) {
|
||||||
handleServerError(context, serverErrorResponse);
|
handleServerError(
|
||||||
|
context,
|
||||||
|
serverErrorResponse,
|
||||||
|
);
|
||||||
return const BackToLoginButton();
|
return const BackToLoginButton();
|
||||||
}
|
}
|
||||||
return Text('Token: $authenticationToken');
|
return Text('Token: $authenticationToken');
|
||||||
},
|
},
|
||||||
loading: () => const CircularProgressIndicator(),
|
loading: () => const CircularProgressIndicator(),
|
||||||
error: (error, _) {
|
error: (error, _) {
|
||||||
handleServerError(context, serverErrorResponse, e: error);
|
handleServerError(
|
||||||
|
context,
|
||||||
|
serverErrorResponse,
|
||||||
|
e: error,
|
||||||
|
);
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Text('Error with OAuth flow: $error'),
|
Text('Error with OAuth flow: $error'),
|
||||||
|
|
@ -74,7 +81,9 @@ class CallbackPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class BackToLoginButton extends StatelessWidget {
|
class BackToLoginButton extends StatelessWidget {
|
||||||
const BackToLoginButton({super.key});
|
const BackToLoginButton({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -88,7 +97,10 @@ class BackToLoginButton extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SomethingWentWrong extends StatelessWidget {
|
class _SomethingWentWrong extends StatelessWidget {
|
||||||
const _SomethingWentWrong({this.message = 'Error with OAuth flow'});
|
const _SomethingWentWrong({
|
||||||
|
super.key,
|
||||||
|
this.message = 'Error with OAuth flow',
|
||||||
|
});
|
||||||
|
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
|
|
@ -98,7 +110,10 @@ class _SomethingWentWrong extends StatelessWidget {
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [Text(message), const BackToLoginButton()],
|
children: [
|
||||||
|
Text(message),
|
||||||
|
const BackToLoginButton(),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -9,36 +9,24 @@ import 'package:vaani/shared/utils.dart';
|
||||||
import 'package:vaani/shared/widgets/add_new_server.dart';
|
import 'package:vaani/shared/widgets/add_new_server.dart';
|
||||||
|
|
||||||
class OnboardingSinglePage extends HookConsumerWidget {
|
class OnboardingSinglePage extends HookConsumerWidget {
|
||||||
const OnboardingSinglePage({super.key});
|
const OnboardingSinglePage({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
return Scaffold(
|
final apiSettings = ref.watch(apiSettingsProvider);
|
||||||
body: LayoutBuilder(
|
final serverUriController = useTextEditingController(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
text: apiSettings.activeServer?.serverUrl.toString() ?? '',
|
||||||
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()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
var audiobookshelfUri = makeBaseUrl(serverUriController.text);
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget fadeSlideTransitionBuilder(Widget child, Animation<double> animation) {
|
final canUserLogin = useState(apiSettings.activeServer != null);
|
||||||
|
|
||||||
|
fadeSlideTransitionBuilder(
|
||||||
|
Widget child,
|
||||||
|
Animation<double> animation,
|
||||||
|
) {
|
||||||
return FadeTransition(
|
return FadeTransition(
|
||||||
opacity: animation,
|
opacity: animation,
|
||||||
child: SlideTransition(
|
child: SlideTransition(
|
||||||
|
|
@ -51,22 +39,9 @@ Widget fadeSlideTransitionBuilder(Widget child, Animation<double> animation) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class OnboardingBody extends HookConsumerWidget {
|
return Scaffold(
|
||||||
const OnboardingBody({super.key});
|
body: Column(
|
||||||
|
|
||||||
@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,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
|
@ -75,7 +50,9 @@ class OnboardingBody extends HookConsumerWidget {
|
||||||
style: Theme.of(context).textTheme.headlineSmall,
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox.square(dimension: 16.0),
|
const SizedBox.square(
|
||||||
|
dimension: 16.0,
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: AnimatedSwitcher(
|
child: AnimatedSwitcher(
|
||||||
|
|
@ -104,12 +81,13 @@ class OnboardingBody extends HookConsumerWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox.square(dimension: 16.0),
|
|
||||||
AnimatedSwitcher(
|
AnimatedSwitcher(
|
||||||
duration: 500.ms,
|
duration: 500.ms,
|
||||||
transitionBuilder: fadeSlideTransitionBuilder,
|
transitionBuilder: fadeSlideTransitionBuilder,
|
||||||
child: canUserLogin.value
|
child: canUserLogin.value
|
||||||
? UserLoginWidget(server: audiobookshelfUri)
|
? UserLoginWidget(
|
||||||
|
server: audiobookshelfUri,
|
||||||
|
)
|
||||||
// ).animate().fade(duration: 600.ms).slideY(begin: 0.3, end: 0)
|
// ).animate().fade(duration: 600.ms).slideY(begin: 0.3, end: 0)
|
||||||
: const RedirectToABS().animate().fadeIn().slideY(
|
: const RedirectToABS().animate().fadeIn().slideY(
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
|
|
@ -117,12 +95,15 @@ class OnboardingBody extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RedirectToABS extends StatelessWidget {
|
class RedirectToABS extends StatelessWidget {
|
||||||
const RedirectToABS({super.key});
|
const RedirectToABS({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -138,14 +119,18 @@ class RedirectToABS extends StatelessWidget {
|
||||||
isSemanticButton: false,
|
isSemanticButton: false,
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
elevation: WidgetStateProperty.all(0),
|
elevation: WidgetStateProperty.all(0),
|
||||||
padding: WidgetStateProperty.all(const EdgeInsets.all(0)),
|
padding: WidgetStateProperty.all(
|
||||||
|
const EdgeInsets.all(0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
// open the github page
|
// open the github page
|
||||||
// ignore: avoid_print
|
// ignore: avoid_print
|
||||||
print('Opening the github page');
|
print('Opening the github page');
|
||||||
await handleLaunchUrl(
|
await handleLaunchUrl(
|
||||||
Uri.parse('https://www.audiobookshelf.org'),
|
Uri.parse(
|
||||||
|
'https://www.audiobookshelf.org',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const Text('Click here'),
|
child: const Text('Click here'),
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,32 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shelfsdk/audiobookshelf_api.dart' show AuthMethod;
|
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||||
import 'package:vaani/api/api_provider.dart' show serverStatusProvider;
|
import 'package:vaani/api/api_provider.dart';
|
||||||
import 'package:vaani/api/server_provider.dart'
|
import 'package:vaani/api/server_provider.dart';
|
||||||
show ServerAlreadyExistsException, audiobookShelfServerProvider;
|
import 'package:vaani/features/onboarding/view/user_login_with_open_id.dart';
|
||||||
import 'package:vaani/features/onboarding/view/onboarding_single_page.dart'
|
import 'package:vaani/features/onboarding/view/user_login_with_password.dart';
|
||||||
show fadeSlideTransitionBuilder;
|
import 'package:vaani/features/onboarding/view/user_login_with_token.dart';
|
||||||
import 'package:vaani/features/onboarding/view/user_login_with_open_id.dart'
|
import 'package:vaani/hacks/fix_autofill_losing_focus.dart';
|
||||||
show UserLoginWithOpenID;
|
import 'package:vaani/models/error_response.dart';
|
||||||
import 'package:vaani/features/onboarding/view/user_login_with_password.dart'
|
import 'package:vaani/settings/api_settings_provider.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;
|
import 'package:vaani/settings/models/models.dart' as model;
|
||||||
|
|
||||||
class UserLoginWidget extends HookConsumerWidget {
|
class UserLoginWidget extends HookConsumerWidget {
|
||||||
const UserLoginWidget({super.key, required this.server, this.onSuccess});
|
UserLoginWidget({
|
||||||
|
super.key,
|
||||||
|
required this.server,
|
||||||
|
});
|
||||||
|
|
||||||
final Uri server;
|
final Uri server;
|
||||||
final Function(model.AuthenticatedUser)? onSuccess;
|
final serverStatusError = ErrorResponseHandler();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final serverStatusError = useMemoized(() => ErrorResponseHandler(), []);
|
final serverStatus =
|
||||||
final serverStatus = ref.watch(
|
ref.watch(serverStatusProvider(server, serverStatusError.storeError));
|
||||||
serverStatusProvider(server, serverStatusError.storeError),
|
|
||||||
);
|
final api = ref.watch(audiobookshelfApiProvider(server));
|
||||||
|
|
||||||
return serverStatus.when(
|
return serverStatus.when(
|
||||||
data: (value) {
|
data: (value) {
|
||||||
|
|
@ -48,11 +42,12 @@ class UserLoginWidget extends HookConsumerWidget {
|
||||||
openIDAvailable:
|
openIDAvailable:
|
||||||
value.authMethods?.contains(AuthMethod.openid) ?? false,
|
value.authMethods?.contains(AuthMethod.openid) ?? false,
|
||||||
openIDButtonText: value.authFormData?.authOpenIDButtonText,
|
openIDButtonText: value.authFormData?.authOpenIDButtonText,
|
||||||
onSuccess: onSuccess,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
loading: () {
|
loading: () {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
error: (error, _) {
|
error: (error, _) {
|
||||||
return Center(
|
return Center(
|
||||||
|
|
@ -63,7 +58,10 @@ class UserLoginWidget extends HookConsumerWidget {
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.invalidate(
|
ref.invalidate(
|
||||||
serverStatusProvider(server, serverStatusError.storeError),
|
serverStatusProvider(
|
||||||
|
server,
|
||||||
|
serverStatusError.storeError,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const Text('Try again'),
|
child: const Text('Try again'),
|
||||||
|
|
@ -76,7 +74,11 @@ class UserLoginWidget extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AuthMethodChoice { local, openid, authToken }
|
enum AuthMethodChoice {
|
||||||
|
local,
|
||||||
|
openid,
|
||||||
|
authToken,
|
||||||
|
}
|
||||||
|
|
||||||
class UserLoginMultipleAuth extends HookConsumerWidget {
|
class UserLoginMultipleAuth extends HookConsumerWidget {
|
||||||
const UserLoginMultipleAuth({
|
const UserLoginMultipleAuth({
|
||||||
|
|
@ -86,7 +88,6 @@ class UserLoginMultipleAuth extends HookConsumerWidget {
|
||||||
this.openIDAvailable = false,
|
this.openIDAvailable = false,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
this.openIDButtonText,
|
this.openIDButtonText,
|
||||||
this.onSuccess,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final Uri server;
|
final Uri server;
|
||||||
|
|
@ -94,7 +95,6 @@ class UserLoginMultipleAuth extends HookConsumerWidget {
|
||||||
final bool openIDAvailable;
|
final bool openIDAvailable;
|
||||||
final void Function()? onPressed;
|
final void Function()? onPressed;
|
||||||
final String? openIDButtonText;
|
final String? openIDButtonText;
|
||||||
final Function(model.AuthenticatedUser)? onSuccess;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
|
@ -104,18 +104,24 @@ class UserLoginMultipleAuth extends HookConsumerWidget {
|
||||||
localAvailable ? AuthMethodChoice.local : AuthMethodChoice.authToken,
|
localAvailable ? AuthMethodChoice.local : AuthMethodChoice.authToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final apiSettings = ref.watch(apiSettingsProvider);
|
||||||
|
|
||||||
model.AudiobookShelfServer addServer() {
|
model.AudiobookShelfServer addServer() {
|
||||||
var newServer = model.AudiobookShelfServer(serverUrl: server);
|
var newServer = model.AudiobookShelfServer(
|
||||||
|
serverUrl: server,
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
// add the server to the list of servers
|
// add the server to the list of servers
|
||||||
ref.read(audiobookShelfServerProvider.notifier).addServer(newServer);
|
ref.read(audiobookShelfServerProvider.notifier).addServer(
|
||||||
|
newServer,
|
||||||
|
);
|
||||||
} on ServerAlreadyExistsException catch (e) {
|
} on ServerAlreadyExistsException catch (e) {
|
||||||
newServer = e.server;
|
newServer = e.server;
|
||||||
} finally {
|
} finally {
|
||||||
ref
|
ref.read(apiSettingsProvider.notifier).updateState(
|
||||||
.read(apiSettingsProvider.notifier)
|
apiSettings.copyWith(
|
||||||
.updateState(
|
activeServer: newServer,
|
||||||
ref.read(apiSettingsProvider).copyWith(activeServer: newServer),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return newServer;
|
return newServer;
|
||||||
|
|
@ -124,25 +130,22 @@ class UserLoginMultipleAuth extends HookConsumerWidget {
|
||||||
return Center(
|
return Center(
|
||||||
child: InactiveFocusScopeObserver(
|
child: InactiveFocusScopeObserver(
|
||||||
child: AutofillGroup(
|
child: AutofillGroup(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Wrap(
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Wrap(
|
|
||||||
// mainAxisAlignment: MainAxisAlignment.center,
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
spacing: 10,
|
spacing: 10,
|
||||||
runAlignment: WrapAlignment.center,
|
runAlignment: WrapAlignment.center,
|
||||||
runSpacing: 10,
|
runSpacing: 10,
|
||||||
alignment: WrapAlignment.center,
|
alignment: WrapAlignment.center,
|
||||||
children:
|
children: [
|
||||||
[
|
|
||||||
// a small label to show the user what to do
|
// a small label to show the user what to do
|
||||||
if (localAvailable)
|
if (localAvailable)
|
||||||
ChoiceChip(
|
ChoiceChip(
|
||||||
label: const Text('Local'),
|
label: const Text('Local'),
|
||||||
selected:
|
selected: methodChoice.value == AuthMethodChoice.local,
|
||||||
methodChoice.value ==
|
|
||||||
AuthMethodChoice.local,
|
|
||||||
onSelected: (selected) {
|
onSelected: (selected) {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
methodChoice.value = AuthMethodChoice.local;
|
methodChoice.value = AuthMethodChoice.local;
|
||||||
|
|
@ -152,62 +155,48 @@ class UserLoginMultipleAuth extends HookConsumerWidget {
|
||||||
if (openIDAvailable)
|
if (openIDAvailable)
|
||||||
ChoiceChip(
|
ChoiceChip(
|
||||||
label: const Text('OpenID'),
|
label: const Text('OpenID'),
|
||||||
selected:
|
selected: methodChoice.value == AuthMethodChoice.openid,
|
||||||
methodChoice.value ==
|
|
||||||
AuthMethodChoice.openid,
|
|
||||||
onSelected: (selected) {
|
onSelected: (selected) {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
methodChoice.value =
|
methodChoice.value = AuthMethodChoice.openid;
|
||||||
AuthMethodChoice.openid;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ChoiceChip(
|
ChoiceChip(
|
||||||
label: const Text('Token'),
|
label: const Text('Token'),
|
||||||
selected:
|
selected:
|
||||||
methodChoice.value ==
|
methodChoice.value == AuthMethodChoice.authToken,
|
||||||
AuthMethodChoice.authToken,
|
|
||||||
onSelected: (selected) {
|
onSelected: (selected) {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
methodChoice.value =
|
methodChoice.value = AuthMethodChoice.authToken;
|
||||||
AuthMethodChoice.authToken;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
],
|
||||||
.animate(interval: 100.ms)
|
|
||||||
.fadeIn(duration: 150.ms, curve: Curves.easeIn),
|
|
||||||
),
|
),
|
||||||
|
const SizedBox.square(
|
||||||
|
dimension: 8,
|
||||||
),
|
),
|
||||||
Padding(
|
switch (methodChoice.value) {
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: AnimatedSwitcher(
|
|
||||||
duration: 200.ms,
|
|
||||||
transitionBuilder: fadeSlideTransitionBuilder,
|
|
||||||
child: switch (methodChoice.value) {
|
|
||||||
AuthMethodChoice.authToken => UserLoginWithToken(
|
AuthMethodChoice.authToken => UserLoginWithToken(
|
||||||
server: server,
|
server: server,
|
||||||
addServer: addServer,
|
addServer: addServer,
|
||||||
onSuccess: onSuccess,
|
|
||||||
),
|
),
|
||||||
AuthMethodChoice.local => UserLoginWithPassword(
|
AuthMethodChoice.local => UserLoginWithPassword(
|
||||||
server: server,
|
server: server,
|
||||||
addServer: addServer,
|
addServer: addServer,
|
||||||
onSuccess: onSuccess,
|
|
||||||
),
|
),
|
||||||
AuthMethodChoice.openid => UserLoginWithOpenID(
|
AuthMethodChoice.openid => UserLoginWithOpenID(
|
||||||
server: server,
|
server: server,
|
||||||
addServer: addServer,
|
addServer: addServer,
|
||||||
openIDButtonText: openIDButtonText,
|
openIDButtonText: openIDButtonText,
|
||||||
onSuccess: onSuccess,
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import 'package:vaani/models/error_response.dart';
|
||||||
import 'package:vaani/router/router.dart';
|
import 'package:vaani/router/router.dart';
|
||||||
import 'package:vaani/settings/constants.dart';
|
import 'package:vaani/settings/constants.dart';
|
||||||
import 'package:vaani/settings/models/models.dart' as model;
|
import 'package:vaani/settings/models/models.dart' as model;
|
||||||
import 'package:vaani/shared/extensions/obfuscation.dart';
|
|
||||||
import 'package:vaani/shared/utils.dart';
|
import 'package:vaani/shared/utils.dart';
|
||||||
|
|
||||||
class UserLoginWithOpenID extends HookConsumerWidget {
|
class UserLoginWithOpenID extends HookConsumerWidget {
|
||||||
|
|
@ -20,14 +19,12 @@ class UserLoginWithOpenID extends HookConsumerWidget {
|
||||||
required this.server,
|
required this.server,
|
||||||
required this.addServer,
|
required this.addServer,
|
||||||
this.openIDButtonText,
|
this.openIDButtonText,
|
||||||
this.onSuccess,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final Uri server;
|
final Uri server;
|
||||||
final model.AudiobookShelfServer Function() addServer;
|
final model.AudiobookShelfServer Function() addServer;
|
||||||
final String? openIDButtonText;
|
final String? openIDButtonText;
|
||||||
final responseErrorHandler = ErrorResponseHandler(name: 'OpenID');
|
final responseErrorHandler = ErrorResponseHandler(name: 'OpenID');
|
||||||
final Function(model.AuthenticatedUser)? onSuccess;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
|
@ -54,9 +51,9 @@ class UserLoginWithOpenID extends HookConsumerWidget {
|
||||||
|
|
||||||
if (openIDLoginEndpoint == null) {
|
if (openIDLoginEndpoint == null) {
|
||||||
if (responseErrorHandler.response.statusCode == 400 &&
|
if (responseErrorHandler.response.statusCode == 400 &&
|
||||||
responseErrorHandler.response.body.toLowerCase().contains(
|
responseErrorHandler.response.body
|
||||||
RegExp(r'invalid.*redirect.*uri'),
|
.toLowerCase()
|
||||||
)) {
|
.contains(RegExp(r'invalid.*redirect.*uri'))) {
|
||||||
// show error
|
// show error
|
||||||
handleServerError(
|
handleServerError(
|
||||||
context,
|
context,
|
||||||
|
|
@ -92,21 +89,19 @@ class UserLoginWithOpenID extends HookConsumerWidget {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
appLogger.fine(
|
appLogger.fine('Got OpenID login endpoint: $openIDLoginEndpoint');
|
||||||
'Got OpenID login endpoint: ${openIDLoginEndpoint.obfuscate()}',
|
|
||||||
);
|
|
||||||
|
|
||||||
// add the flow to the provider
|
// add the flow to the provider
|
||||||
ref
|
ref.read(oauthFlowsProvider.notifier).addFlow(
|
||||||
.read(oauthFlowsProvider.notifier)
|
|
||||||
.addFlow(
|
|
||||||
oauthState,
|
oauthState,
|
||||||
verifier: verifier,
|
verifier: verifier,
|
||||||
serverUri: server,
|
serverUri: server,
|
||||||
cookie: Cookie.fromSetCookieValue(authCookie!),
|
cookie: Cookie.fromSetCookieValue(authCookie!),
|
||||||
);
|
);
|
||||||
|
|
||||||
await handleLaunchUrl(openIDLoginEndpoint);
|
await handleLaunchUrl(
|
||||||
|
openIDLoginEndpoint,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,10 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:lottie/lottie.dart';
|
import 'package:lottie/lottie.dart';
|
||||||
import 'package:shelfsdk/audiobookshelf_api.dart';
|
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||||
import 'package:vaani/api/api_provider.dart';
|
import 'package:vaani/api/api_provider.dart';
|
||||||
import 'package:vaani/api/authenticated_users_provider.dart';
|
import 'package:vaani/api/authenticated_user_provider.dart';
|
||||||
import 'package:vaani/hacks/fix_autofill_losing_focus.dart';
|
import 'package:vaani/hacks/fix_autofill_losing_focus.dart';
|
||||||
import 'package:vaani/models/error_response.dart';
|
import 'package:vaani/models/error_response.dart';
|
||||||
import 'package:vaani/router/router.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/settings/models/models.dart' as model;
|
||||||
import 'package:vaani/shared/utils.dart';
|
import 'package:vaani/shared/utils.dart';
|
||||||
|
|
||||||
|
|
@ -18,20 +17,17 @@ class UserLoginWithPassword extends HookConsumerWidget {
|
||||||
super.key,
|
super.key,
|
||||||
required this.server,
|
required this.server,
|
||||||
required this.addServer,
|
required this.addServer,
|
||||||
this.onSuccess,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final Uri server;
|
final Uri server;
|
||||||
final model.AudiobookShelfServer Function() addServer;
|
final model.AudiobookShelfServer Function() addServer;
|
||||||
final serverErrorResponse = ErrorResponseHandler();
|
final serverErrorResponse = ErrorResponseHandler();
|
||||||
final Function(model.AuthenticatedUser)? onSuccess;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final usernameController = useTextEditingController();
|
final usernameController = useTextEditingController();
|
||||||
final passwordController = useTextEditingController();
|
final passwordController = useTextEditingController();
|
||||||
final isPasswordVisibleAnimationController = useAnimationController(
|
final isPasswordVisibleAnimationController = useAnimationController(
|
||||||
initialValue: 1,
|
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -39,14 +35,17 @@ class UserLoginWithPassword extends HookConsumerWidget {
|
||||||
final api = ref.watch(audiobookshelfApiProvider(server));
|
final api = ref.watch(audiobookshelfApiProvider(server));
|
||||||
|
|
||||||
// forward animation when the password visibility changes
|
// forward animation when the password visibility changes
|
||||||
useEffect(() {
|
useEffect(
|
||||||
|
() {
|
||||||
if (isPasswordVisible.value) {
|
if (isPasswordVisible.value) {
|
||||||
isPasswordVisibleAnimationController.forward();
|
isPasswordVisibleAnimationController.forward();
|
||||||
} else {
|
} else {
|
||||||
isPasswordVisibleAnimationController.reverse();
|
isPasswordVisibleAnimationController.reverse();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [isPasswordVisible.value]);
|
},
|
||||||
|
[isPasswordVisible.value],
|
||||||
|
);
|
||||||
|
|
||||||
/// Login to the server and save the user
|
/// Login to the server and save the user
|
||||||
Future<void> loginAndSave() async {
|
Future<void> loginAndSave() async {
|
||||||
|
|
@ -77,24 +76,24 @@ class UserLoginWithPassword extends HookConsumerWidget {
|
||||||
final authenticatedUser = model.AuthenticatedUser(
|
final authenticatedUser = model.AuthenticatedUser(
|
||||||
server: addServer(),
|
server: addServer(),
|
||||||
id: success.user.id,
|
id: success.user.id,
|
||||||
|
password: password,
|
||||||
username: username,
|
username: username,
|
||||||
authToken: api.token!,
|
authToken: api.token!,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (onSuccess != null) {
|
|
||||||
onSuccess!(authenticatedUser);
|
|
||||||
} else {
|
|
||||||
// add the user to the list of users
|
// add the user to the list of users
|
||||||
ref
|
ref
|
||||||
.read(authenticatedUsersProvider.notifier)
|
.read(authenticatedUserProvider.notifier)
|
||||||
.addUser(authenticatedUser, setActive: true);
|
.addUser(authenticatedUser, setActive: true);
|
||||||
context.goNamed(Routes.home.name);
|
|
||||||
}
|
// redirect to the library page
|
||||||
|
GoRouter.of(context).goNamed(Routes.home.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: InactiveFocusScopeObserver(
|
child: InactiveFocusScopeObserver(
|
||||||
child: AutofillGroup(
|
child: AutofillGroup(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -106,9 +105,10 @@ class UserLoginWithPassword extends HookConsumerWidget {
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Username',
|
labelText: 'Username',
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(
|
||||||
color: Theme.of(
|
color: Theme.of(context)
|
||||||
context,
|
.colorScheme
|
||||||
).colorScheme.onSurface.withValues(alpha: 0.8),
|
.onSurface
|
||||||
|
.withOpacity(0.8),
|
||||||
),
|
),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
|
|
@ -125,16 +125,15 @@ class UserLoginWithPassword extends HookConsumerWidget {
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Password',
|
labelText: 'Password',
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(
|
||||||
color: Theme.of(
|
color: Theme.of(context)
|
||||||
context,
|
.colorScheme
|
||||||
).colorScheme.onSurface.withValues(alpha: 0.8),
|
.onSurface
|
||||||
|
.withOpacity(0.8),
|
||||||
),
|
),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
suffixIcon: ColorFiltered(
|
suffixIcon: ColorFiltered(
|
||||||
colorFilter: ColorFilter.mode(
|
colorFilter: ColorFilter.mode(
|
||||||
Theme.of(
|
Theme.of(context).colorScheme.primary.withOpacity(0.8),
|
||||||
context,
|
|
||||||
).colorScheme.primary.withValues(alpha: 0.8),
|
|
||||||
BlendMode.srcIn,
|
BlendMode.srcIn,
|
||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
|
|
@ -151,7 +150,9 @@ class UserLoginWithPassword extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
suffixIconConstraints: const BoxConstraints(maxHeight: 45),
|
suffixIconConstraints: const BoxConstraints(
|
||||||
|
maxHeight: 45,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
|
|
@ -163,6 +164,7 @@ class UserLoginWithPassword extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,12 +191,10 @@ Future<void> handleServerError(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Error'),
|
title: const Text('Error'),
|
||||||
content: SelectableText(
|
content: SelectableText('$title\n'
|
||||||
'$title\n'
|
|
||||||
'Got response: ${responseErrorHandler.response.body} (${responseErrorHandler.response.statusCode})\n'
|
'Got response: ${responseErrorHandler.response.body} (${responseErrorHandler.response.statusCode})\n'
|
||||||
'Stacktrace: $e\n\n'
|
'Stacktrace: $e\n\n'
|
||||||
'$body\n\n',
|
'$body\n\n'),
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
if (outLink != null)
|
if (outLink != null)
|
||||||
TextButton(
|
TextButton(
|
||||||
|
|
@ -207,10 +207,8 @@ Future<void> handleServerError(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// open an issue on the github page
|
// open an issue on the github page
|
||||||
handleLaunchUrl(
|
handleLaunchUrl(
|
||||||
AppMetadata.githubRepo
|
Uri.parse(
|
||||||
// append the issue url
|
'https://github.com/Dr-Blank/Vaani/issues',
|
||||||
.replace(
|
|
||||||
path: '${AppMetadata.githubRepo.path}/issues/new',
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shelfsdk/audiobookshelf_api.dart';
|
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||||
import 'package:vaani/api/api_provider.dart';
|
import 'package:vaani/api/api_provider.dart';
|
||||||
import 'package:vaani/api/authenticated_users_provider.dart';
|
import 'package:vaani/api/authenticated_user_provider.dart';
|
||||||
import 'package:vaani/models/error_response.dart';
|
import 'package:vaani/models/error_response.dart';
|
||||||
import 'package:vaani/router/router.dart';
|
import 'package:vaani/router/router.dart';
|
||||||
import 'package:vaani/settings/models/models.dart' as model;
|
import 'package:vaani/settings/models/models.dart' as model;
|
||||||
|
|
@ -14,13 +14,11 @@ class UserLoginWithToken extends HookConsumerWidget {
|
||||||
super.key,
|
super.key,
|
||||||
required this.server,
|
required this.server,
|
||||||
required this.addServer,
|
required this.addServer,
|
||||||
this.onSuccess,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final Uri server;
|
final Uri server;
|
||||||
final model.AudiobookShelfServer Function() addServer;
|
final model.AudiobookShelfServer Function() addServer;
|
||||||
final serverErrorResponse = ErrorResponseHandler();
|
final serverErrorResponse = ErrorResponseHandler();
|
||||||
final Function(model.AuthenticatedUser)? onSuccess;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
|
@ -67,15 +65,12 @@ class UserLoginWithToken extends HookConsumerWidget {
|
||||||
authToken: api.token!,
|
authToken: api.token!,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (onSuccess != null) {
|
|
||||||
onSuccess!(authenticatedUser);
|
|
||||||
} else {
|
|
||||||
ref
|
ref
|
||||||
.read(authenticatedUsersProvider.notifier)
|
.read(authenticatedUserProvider.notifier)
|
||||||
.addUser(authenticatedUser, setActive: true);
|
.addUser(authenticatedUser, setActive: true);
|
||||||
|
|
||||||
context.goNamed(Routes.home.name);
|
context.goNamed(Routes.home.name);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return Form(
|
return Form(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
@ -89,9 +84,7 @@ class UserLoginWithToken extends HookConsumerWidget {
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'API Token',
|
labelText: 'API Token',
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(
|
||||||
color: Theme.of(
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
|
||||||
context,
|
|
||||||
).colorScheme.onSurface.withValues(alpha: 0.8),
|
|
||||||
),
|
),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
|
|
@ -106,7 +99,10 @@ class UserLoginWithToken extends HookConsumerWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
ElevatedButton(onPressed: loginAndSave, child: const Text('Login')),
|
ElevatedButton(
|
||||||
|
onPressed: loginAndSave,
|
||||||
|
child: const Text('Login'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ part 'book_settings.g.dart';
|
||||||
|
|
||||||
/// per book settings
|
/// per book settings
|
||||||
@freezed
|
@freezed
|
||||||
sealed class BookSettings with _$BookSettings {
|
class BookSettings with _$BookSettings {
|
||||||
const factory BookSettings({
|
const factory BookSettings({
|
||||||
required String bookId,
|
required String bookId,
|
||||||
@Default(NullablePlayerSettings()) NullablePlayerSettings playerSettings,
|
@Default(NullablePlayerSettings()) NullablePlayerSettings playerSettings,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// 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
|
// 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,284 +9,195 @@ part of 'book_settings.dart';
|
||||||
// FreezedGenerator
|
// FreezedGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// dart format off
|
|
||||||
T _$identity<T>(T value) => value;
|
T _$identity<T>(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<String, dynamic> json) {
|
||||||
|
return _BookSettings.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$BookSettings {
|
mixin _$BookSettings {
|
||||||
|
String get bookId => throw _privateConstructorUsedError;
|
||||||
String get bookId; NullablePlayerSettings get playerSettings;
|
NullablePlayerSettings get playerSettings =>
|
||||||
/// Create a copy of BookSettings
|
throw _privateConstructorUsedError;
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$BookSettingsCopyWith<BookSettings> get copyWith => _$BookSettingsCopyWithImpl<BookSettings>(this as BookSettings, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this BookSettings to a JSON map.
|
/// Serializes this BookSettings to a JSON map.
|
||||||
Map<String, dynamic> toJson();
|
Map<String, dynamic> toJson() => 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> {
|
|
||||||
factory $BookSettingsCopyWith(BookSettings value, $Res Function(BookSettings) _then) = _$BookSettingsCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
String bookId, NullablePlayerSettings playerSettings
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$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
|
/// Create a copy of BookSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? bookId = null,Object? playerSettings = null,}) {
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
return _then(_self.copyWith(
|
$BookSettingsCopyWith<BookSettings> get copyWith =>
|
||||||
bookId: null == bookId ? _self.bookId : bookId // ignore: cast_nullable_to_non_nullable
|
throw _privateConstructorUsedError;
|
||||||
as String,playerSettings: null == playerSettings ? _self.playerSettings : playerSettings // ignore: cast_nullable_to_non_nullable
|
|
||||||
as NullablePlayerSettings,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $BookSettingsCopyWith<$Res> {
|
||||||
|
factory $BookSettingsCopyWith(
|
||||||
|
BookSettings value, $Res Function(BookSettings) then) =
|
||||||
|
_$BookSettingsCopyWithImpl<$Res, BookSettings>;
|
||||||
|
@useResult
|
||||||
|
$Res call({String bookId, NullablePlayerSettings playerSettings});
|
||||||
|
|
||||||
|
$NullablePlayerSettingsCopyWith<$Res> get playerSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$BookSettingsCopyWithImpl<$Res, $Val extends BookSettings>
|
||||||
|
implements $BookSettingsCopyWith<$Res> {
|
||||||
|
_$BookSettingsCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _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
|
/// Create a copy of BookSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
$NullablePlayerSettingsCopyWith<$Res> get playerSettings {
|
$NullablePlayerSettingsCopyWith<$Res> get playerSettings {
|
||||||
|
return $NullablePlayerSettingsCopyWith<$Res>(_value.playerSettings,
|
||||||
return $NullablePlayerSettingsCopyWith<$Res>(_self.playerSettings, (value) {
|
(value) {
|
||||||
return _then(_self.copyWith(playerSettings: value));
|
return _then(_value.copyWith(playerSettings: value) as $Val);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @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});
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [BookSettings].
|
@override
|
||||||
extension BookSettingsPatterns on BookSettings {
|
$NullablePlayerSettingsCopyWith<$Res> get 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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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
|
/// @nodoc
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
class _$BookSettingsImpl implements _BookSettings {
|
||||||
|
const _$BookSettingsImpl(
|
||||||
|
{required this.bookId,
|
||||||
|
this.playerSettings = const NullablePlayerSettings()});
|
||||||
|
|
||||||
class _BookSettings implements BookSettings {
|
factory _$BookSettingsImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
const _BookSettings({required this.bookId, this.playerSettings = const NullablePlayerSettings()});
|
_$$BookSettingsImplFromJson(json);
|
||||||
factory _BookSettings.fromJson(Map<String, dynamic> json) => _$BookSettingsFromJson(json);
|
|
||||||
|
|
||||||
@override final String bookId;
|
|
||||||
@override@JsonKey() final NullablePlayerSettings 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
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
final String bookId;
|
||||||
return _$BookSettingsToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
@JsonKey()
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BookSettings&&(identical(other.bookId, bookId) || other.bookId == bookId)&&(identical(other.playerSettings, playerSettings) || other.playerSettings == playerSettings));
|
final NullablePlayerSettings playerSettings;
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,bookId,playerSettings);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'BookSettings(bookId: $bookId, playerSettings: $playerSettings)';
|
return 'BookSettings(bookId: $bookId, playerSettings: $playerSettings)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
abstract mixin class _$BookSettingsCopyWith<$Res> implements $BookSettingsCopyWith<$Res> {
|
@override
|
||||||
factory _$BookSettingsCopyWith(_BookSettings value, $Res Function(_BookSettings) _then) = __$BookSettingsCopyWithImpl;
|
int get hashCode => Object.hash(runtimeType, bookId, playerSettings);
|
||||||
@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
|
/// Create a copy of BookSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? bookId = null,Object? playerSettings = null,}) {
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
return _then(_BookSettings(
|
@override
|
||||||
bookId: null == bookId ? _self.bookId : bookId // ignore: cast_nullable_to_non_nullable
|
@pragma('vm:prefer-inline')
|
||||||
as String,playerSettings: null == playerSettings ? _self.playerSettings : playerSettings // ignore: cast_nullable_to_non_nullable
|
_$$BookSettingsImplCopyWith<_$BookSettingsImpl> get copyWith =>
|
||||||
as NullablePlayerSettings,
|
__$$BookSettingsImplCopyWithImpl<_$BookSettingsImpl>(this, _$identity);
|
||||||
));
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$BookSettingsImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _BookSettings implements BookSettings {
|
||||||
|
const factory _BookSettings(
|
||||||
|
{required final String bookId,
|
||||||
|
final NullablePlayerSettings playerSettings}) = _$BookSettingsImpl;
|
||||||
|
|
||||||
|
factory _BookSettings.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$BookSettingsImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get bookId;
|
||||||
|
@override
|
||||||
|
NullablePlayerSettings get playerSettings;
|
||||||
|
|
||||||
/// Create a copy of BookSettings
|
/// Create a copy of BookSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
$NullablePlayerSettingsCopyWith<$Res> get playerSettings {
|
_$$BookSettingsImplCopyWith<_$BookSettingsImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
return $NullablePlayerSettingsCopyWith<$Res>(_self.playerSettings, (value) {
|
|
||||||
return _then(_self.copyWith(playerSettings: value));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// dart format on
|
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,16 @@ part of 'book_settings.dart';
|
||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
_BookSettings _$BookSettingsFromJson(Map<String, dynamic> json) =>
|
_$BookSettingsImpl _$$BookSettingsImplFromJson(Map<String, dynamic> json) =>
|
||||||
_BookSettings(
|
_$BookSettingsImpl(
|
||||||
bookId: json['bookId'] as String,
|
bookId: json['bookId'] as String,
|
||||||
playerSettings: json['playerSettings'] == null
|
playerSettings: json['playerSettings'] == null
|
||||||
? const NullablePlayerSettings()
|
? const NullablePlayerSettings()
|
||||||
: NullablePlayerSettings.fromJson(
|
: NullablePlayerSettings.fromJson(
|
||||||
json['playerSettings'] as Map<String, dynamic>,
|
json['playerSettings'] as Map<String, dynamic>),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$BookSettingsToJson(_BookSettings instance) =>
|
Map<String, dynamic> _$$BookSettingsImplToJson(_$BookSettingsImpl instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'bookId': instance.bookId,
|
'bookId': instance.bookId,
|
||||||
'playerSettings': instance.playerSettings,
|
'playerSettings': instance.playerSettings,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ part 'nullable_player_settings.freezed.dart';
|
||||||
part 'nullable_player_settings.g.dart';
|
part 'nullable_player_settings.g.dart';
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class NullablePlayerSettings with _$NullablePlayerSettings {
|
class NullablePlayerSettings with _$NullablePlayerSettings {
|
||||||
const factory NullablePlayerSettings({
|
const factory NullablePlayerSettings({
|
||||||
MinimizedPlayerSettings? miniPlayerSettings,
|
MinimizedPlayerSettings? miniPlayerSettings,
|
||||||
ExpandedPlayerSettings? expandedPlayerSettings,
|
ExpandedPlayerSettings? expandedPlayerSettings,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// 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
|
// 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,251 +9,270 @@ part of 'nullable_player_settings.dart';
|
||||||
// FreezedGenerator
|
// FreezedGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// dart format off
|
|
||||||
T _$identity<T>(T value) => value;
|
T _$identity<T>(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<String, dynamic> json) {
|
||||||
|
return _NullablePlayerSettings.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$NullablePlayerSettings {
|
mixin _$NullablePlayerSettings {
|
||||||
|
MinimizedPlayerSettings? get miniPlayerSettings =>
|
||||||
MinimizedPlayerSettings? get miniPlayerSettings; ExpandedPlayerSettings? get expandedPlayerSettings; double? get preferredDefaultVolume; double? get preferredDefaultSpeed; List<double>? get speedOptions; SleepTimerSettings? get sleepTimerSettings; Duration? get playbackReportInterval;
|
throw _privateConstructorUsedError;
|
||||||
/// Create a copy of NullablePlayerSettings
|
ExpandedPlayerSettings? get expandedPlayerSettings =>
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
throw _privateConstructorUsedError;
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
double? get preferredDefaultVolume => throw _privateConstructorUsedError;
|
||||||
@pragma('vm:prefer-inline')
|
double? get preferredDefaultSpeed => throw _privateConstructorUsedError;
|
||||||
$NullablePlayerSettingsCopyWith<NullablePlayerSettings> get copyWith => _$NullablePlayerSettingsCopyWithImpl<NullablePlayerSettings>(this as NullablePlayerSettings, _$identity);
|
List<double>? get speedOptions => throw _privateConstructorUsedError;
|
||||||
|
SleepTimerSettings? get sleepTimerSettings =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Duration? get playbackReportInterval => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Serializes this NullablePlayerSettings to a JSON map.
|
/// Serializes this NullablePlayerSettings to a JSON map.
|
||||||
Map<String, dynamic> toJson();
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
|
||||||
@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> {
|
|
||||||
factory $NullablePlayerSettingsCopyWith(NullablePlayerSettings value, $Res Function(NullablePlayerSettings) _then) = _$NullablePlayerSettingsCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List<double>? speedOptions, SleepTimerSettings? sleepTimerSettings, Duration? playbackReportInterval
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings;$ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings;$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
|
/// Create a copy of NullablePlayerSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// 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,}) {
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
return _then(_self.copyWith(
|
$NullablePlayerSettingsCopyWith<NullablePlayerSettings> get copyWith =>
|
||||||
miniPlayerSettings: freezed == miniPlayerSettings ? _self.miniPlayerSettings : miniPlayerSettings // ignore: cast_nullable_to_non_nullable
|
throw _privateConstructorUsedError;
|
||||||
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<double>?,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?,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @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<double>? speedOptions,
|
||||||
|
SleepTimerSettings? sleepTimerSettings,
|
||||||
|
Duration? playbackReportInterval});
|
||||||
|
|
||||||
|
$MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings;
|
||||||
|
$ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings;
|
||||||
|
$SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$NullablePlayerSettingsCopyWithImpl<$Res,
|
||||||
|
$Val extends NullablePlayerSettings>
|
||||||
|
implements $NullablePlayerSettingsCopyWith<$Res> {
|
||||||
|
_$NullablePlayerSettingsCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _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<double>?,
|
||||||
|
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
|
/// Create a copy of NullablePlayerSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
$MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings {
|
$MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings {
|
||||||
if (_self.miniPlayerSettings == null) {
|
if (_value.miniPlayerSettings == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $MinimizedPlayerSettingsCopyWith<$Res>(_self.miniPlayerSettings!, (value) {
|
return $MinimizedPlayerSettingsCopyWith<$Res>(_value.miniPlayerSettings!,
|
||||||
return _then(_self.copyWith(miniPlayerSettings: value));
|
(value) {
|
||||||
|
return _then(_value.copyWith(miniPlayerSettings: value) as $Val);
|
||||||
});
|
});
|
||||||
}/// Create a copy of NullablePlayerSettings
|
}
|
||||||
|
|
||||||
|
/// Create a copy of NullablePlayerSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
$ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings {
|
$ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings {
|
||||||
if (_self.expandedPlayerSettings == null) {
|
if (_value.expandedPlayerSettings == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ExpandedPlayerSettingsCopyWith<$Res>(_self.expandedPlayerSettings!, (value) {
|
return $ExpandedPlayerSettingsCopyWith<$Res>(_value.expandedPlayerSettings!,
|
||||||
return _then(_self.copyWith(expandedPlayerSettings: value));
|
(value) {
|
||||||
|
return _then(_value.copyWith(expandedPlayerSettings: value) as $Val);
|
||||||
});
|
});
|
||||||
}/// Create a copy of NullablePlayerSettings
|
}
|
||||||
|
|
||||||
|
/// Create a copy of NullablePlayerSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
$SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings {
|
$SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings {
|
||||||
if (_self.sleepTimerSettings == null) {
|
if (_value.sleepTimerSettings == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $SleepTimerSettingsCopyWith<$Res>(_self.sleepTimerSettings!, (value) {
|
return $SleepTimerSettingsCopyWith<$Res>(_value.sleepTimerSettings!,
|
||||||
return _then(_self.copyWith(sleepTimerSettings: value));
|
(value) {
|
||||||
|
return _then(_value.copyWith(sleepTimerSettings: value) as $Val);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @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<double>? speedOptions,
|
||||||
|
SleepTimerSettings? sleepTimerSettings,
|
||||||
|
Duration? playbackReportInterval});
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [NullablePlayerSettings].
|
@override
|
||||||
extension NullablePlayerSettingsPatterns on NullablePlayerSettings {
|
$MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings;
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
@override
|
||||||
///
|
$ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings;
|
||||||
/// It is equivalent to doing:
|
@override
|
||||||
/// ```dart
|
$SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings;
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(TResult Function( MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List<double>? 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 extends Object?>(TResult Function( MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List<double>? 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 extends Object?>(TResult? Function( MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List<double>? 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<double>?,
|
||||||
|
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
|
/// @nodoc
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
class _$NullablePlayerSettingsImpl implements _NullablePlayerSettings {
|
||||||
|
const _$NullablePlayerSettingsImpl(
|
||||||
|
{this.miniPlayerSettings,
|
||||||
|
this.expandedPlayerSettings,
|
||||||
|
this.preferredDefaultVolume,
|
||||||
|
this.preferredDefaultSpeed,
|
||||||
|
final List<double>? speedOptions,
|
||||||
|
this.sleepTimerSettings,
|
||||||
|
this.playbackReportInterval})
|
||||||
|
: _speedOptions = speedOptions;
|
||||||
|
|
||||||
class _NullablePlayerSettings implements NullablePlayerSettings {
|
factory _$NullablePlayerSettingsImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
const _NullablePlayerSettings({this.miniPlayerSettings, this.expandedPlayerSettings, this.preferredDefaultVolume, this.preferredDefaultSpeed, final List<double>? speedOptions, this.sleepTimerSettings, this.playbackReportInterval}): _speedOptions = speedOptions;
|
_$$NullablePlayerSettingsImplFromJson(json);
|
||||||
factory _NullablePlayerSettings.fromJson(Map<String, dynamic> json) => _$NullablePlayerSettingsFromJson(json);
|
|
||||||
|
|
||||||
@override final MinimizedPlayerSettings? miniPlayerSettings;
|
@override
|
||||||
@override final ExpandedPlayerSettings? expandedPlayerSettings;
|
final MinimizedPlayerSettings? miniPlayerSettings;
|
||||||
@override final double? preferredDefaultVolume;
|
@override
|
||||||
@override final double? preferredDefaultSpeed;
|
final ExpandedPlayerSettings? expandedPlayerSettings;
|
||||||
|
@override
|
||||||
|
final double? preferredDefaultVolume;
|
||||||
|
@override
|
||||||
|
final double? preferredDefaultSpeed;
|
||||||
final List<double>? _speedOptions;
|
final List<double>? _speedOptions;
|
||||||
@override List<double>? get speedOptions {
|
@override
|
||||||
|
List<double>? get speedOptions {
|
||||||
final value = _speedOptions;
|
final value = _speedOptions;
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
if (_speedOptions is EqualUnmodifiableListView) return _speedOptions;
|
if (_speedOptions is EqualUnmodifiableListView) return _speedOptions;
|
||||||
|
|
@ -261,109 +280,98 @@ class _NullablePlayerSettings implements NullablePlayerSettings {
|
||||||
return EqualUnmodifiableListView(value);
|
return EqualUnmodifiableListView(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override final SleepTimerSettings? sleepTimerSettings;
|
|
||||||
@override final Duration? playbackReportInterval;
|
|
||||||
|
|
||||||
/// 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
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
final SleepTimerSettings? sleepTimerSettings;
|
||||||
return _$NullablePlayerSettingsToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
final Duration? playbackReportInterval;
|
||||||
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
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'NullablePlayerSettings(miniPlayerSettings: $miniPlayerSettings, expandedPlayerSettings: $expandedPlayerSettings, preferredDefaultVolume: $preferredDefaultVolume, preferredDefaultSpeed: $preferredDefaultSpeed, speedOptions: $speedOptions, sleepTimerSettings: $sleepTimerSettings, playbackReportInterval: $playbackReportInterval)';
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
abstract mixin class _$NullablePlayerSettingsCopyWith<$Res> implements $NullablePlayerSettingsCopyWith<$Res> {
|
@override
|
||||||
factory _$NullablePlayerSettingsCopyWith(_NullablePlayerSettings value, $Res Function(_NullablePlayerSettings) _then) = __$NullablePlayerSettingsCopyWithImpl;
|
int get hashCode => Object.hash(
|
||||||
@override @useResult
|
runtimeType,
|
||||||
$Res call({
|
miniPlayerSettings,
|
||||||
MinimizedPlayerSettings? miniPlayerSettings, ExpandedPlayerSettings? expandedPlayerSettings, double? preferredDefaultVolume, double? preferredDefaultSpeed, List<double>? speedOptions, SleepTimerSettings? sleepTimerSettings, Duration? playbackReportInterval
|
expandedPlayerSettings,
|
||||||
});
|
preferredDefaultVolume,
|
||||||
|
preferredDefaultSpeed,
|
||||||
|
const DeepCollectionEquality().hash(_speedOptions),
|
||||||
@override $MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings;@override $ExpandedPlayerSettingsCopyWith<$Res>? get expandedPlayerSettings;@override $SleepTimerSettingsCopyWith<$Res>? get sleepTimerSettings;
|
sleepTimerSettings,
|
||||||
|
playbackReportInterval);
|
||||||
}
|
|
||||||
/// @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
|
/// Create a copy of NullablePlayerSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// 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,}) {
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
return _then(_NullablePlayerSettings(
|
@override
|
||||||
miniPlayerSettings: freezed == miniPlayerSettings ? _self.miniPlayerSettings : miniPlayerSettings // ignore: cast_nullable_to_non_nullable
|
@pragma('vm:prefer-inline')
|
||||||
as MinimizedPlayerSettings?,expandedPlayerSettings: freezed == expandedPlayerSettings ? _self.expandedPlayerSettings : expandedPlayerSettings // ignore: cast_nullable_to_non_nullable
|
_$$NullablePlayerSettingsImplCopyWith<_$NullablePlayerSettingsImpl>
|
||||||
as ExpandedPlayerSettings?,preferredDefaultVolume: freezed == preferredDefaultVolume ? _self.preferredDefaultVolume : preferredDefaultVolume // ignore: cast_nullable_to_non_nullable
|
get copyWith => __$$NullablePlayerSettingsImplCopyWithImpl<
|
||||||
as double?,preferredDefaultSpeed: freezed == preferredDefaultSpeed ? _self.preferredDefaultSpeed : preferredDefaultSpeed // ignore: cast_nullable_to_non_nullable
|
_$NullablePlayerSettingsImpl>(this, _$identity);
|
||||||
as double?,speedOptions: freezed == speedOptions ? _self._speedOptions : speedOptions // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<double>?,sleepTimerSettings: freezed == sleepTimerSettings ? _self.sleepTimerSettings : sleepTimerSettings // ignore: cast_nullable_to_non_nullable
|
@override
|
||||||
as SleepTimerSettings?,playbackReportInterval: freezed == playbackReportInterval ? _self.playbackReportInterval : playbackReportInterval // ignore: cast_nullable_to_non_nullable
|
Map<String, dynamic> toJson() {
|
||||||
as Duration?,
|
return _$$NullablePlayerSettingsImplToJson(
|
||||||
));
|
this,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _NullablePlayerSettings implements NullablePlayerSettings {
|
||||||
|
const factory _NullablePlayerSettings(
|
||||||
|
{final MinimizedPlayerSettings? miniPlayerSettings,
|
||||||
|
final ExpandedPlayerSettings? expandedPlayerSettings,
|
||||||
|
final double? preferredDefaultVolume,
|
||||||
|
final double? preferredDefaultSpeed,
|
||||||
|
final List<double>? speedOptions,
|
||||||
|
final SleepTimerSettings? sleepTimerSettings,
|
||||||
|
final Duration? playbackReportInterval}) = _$NullablePlayerSettingsImpl;
|
||||||
|
|
||||||
|
factory _NullablePlayerSettings.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$NullablePlayerSettingsImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
MinimizedPlayerSettings? get miniPlayerSettings;
|
||||||
|
@override
|
||||||
|
ExpandedPlayerSettings? get expandedPlayerSettings;
|
||||||
|
@override
|
||||||
|
double? get preferredDefaultVolume;
|
||||||
|
@override
|
||||||
|
double? get preferredDefaultSpeed;
|
||||||
|
@override
|
||||||
|
List<double>? get speedOptions;
|
||||||
|
@override
|
||||||
|
SleepTimerSettings? get sleepTimerSettings;
|
||||||
|
@override
|
||||||
|
Duration? get playbackReportInterval;
|
||||||
|
|
||||||
/// Create a copy of NullablePlayerSettings
|
/// Create a copy of NullablePlayerSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
$MinimizedPlayerSettingsCopyWith<$Res>? get miniPlayerSettings {
|
_$$NullablePlayerSettingsImplCopyWith<_$NullablePlayerSettingsImpl>
|
||||||
if (_self.miniPlayerSettings == null) {
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
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
|
|
||||||
|
|
|
||||||